From 73eb8e7a9fe75ced5498c1706a3a6bd66a33b02b Mon Sep 17 00:00:00 2001 From: Arc'blroth <45273859+Arc-blroth@users.noreply.github.com> Date: Sun, 14 Mar 2021 01:33:44 -0800 Subject: [PATCH] Fix generated descriptor set layouts for shader modules with multiple entrypoints (#1497) * Write descriptor set layout structs for each entrypoint rather than for the entire module Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com> * Fix descriptor set calculation for all storage classes across all SPIRV versions by inspecting each entrypoint's instruction tree. Also adds 27 instructions to parse.rs. Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com> * Add unit tests for descriptor set calculations Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com> * Fix BufferBlock not counting as a Block decoration on structs (this fixes the unit tests from 4875bcc2) 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> * Update changelog Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com> --- CHANGELOG_VULKANO.md | 4 + vulkano-shaders/src/codegen.rs | 8 +- vulkano-shaders/src/descriptor_sets.rs | 447 ++++++++++++++++-- vulkano-shaders/src/entry_point.rs | 35 +- vulkano-shaders/src/parse.rs | 398 ++++++++++++++++ .../tests/multiple_entrypoints.spv | Bin 0 -> 2188 bytes 6 files changed, 835 insertions(+), 57 deletions(-) create mode 100644 vulkano-shaders/tests/multiple_entrypoints.spv diff --git a/CHANGELOG_VULKANO.md b/CHANGELOG_VULKANO.md index dc063280..460dacfb 100644 --- a/CHANGELOG_VULKANO.md +++ b/CHANGELOG_VULKANO.md @@ -6,6 +6,10 @@ - **Breaking** On `AutoCommandBufferBuilder`, methods that bind a descriptor set now take a `dynamic_offsets` parameter - **Breaking** On `AutoCommandBufferBuilder` and `SyncCommandBufferBuilder`, the `update_buffer` method now takes `data` by reference - **Breaking** Made `PipelineLayoutDescTweaks` public, for use with compute pipelines +- Fixed `shader!` generated descriptor set layouts for shader modules with multiple entrypoints + - **Breaking** Prefixed `shader!` generated descriptor set `Layout` structs with the name of the entrypoint the layout belongs to. For shaders generated from GLSL source, this means `Layout` has been renamed to `MainLayout`. + - **Breaking** `shader!` will no longer generate descriptor information for variables that are declared but not used in a shader. +- **Breaking** `shader!` now accepts structs in shader interfaces decorated with `BufferBlock` rather than `Block` - Added support for `ImageAspect` and YV12/NV12 formats, for use with the UnsafeImage API. - Added basic VK_KHR_external_memory, VK_KHR_external_memory_fd, and VK_EXT_external_memory_dma_buf support. - Fixed potential segmentation fault in `ComputePipeline` when referencing `PipelineCache` objects. diff --git a/vulkano-shaders/src/codegen.rs b/vulkano-shaders/src/codegen.rs index 24bc7c22..06766066 100644 --- a/vulkano-shaders/src/codegen.rs +++ b/vulkano-shaders/src/codegen.rs @@ -26,7 +26,7 @@ use crate::parse; use crate::read_file_to_string; use crate::spec_consts; use crate::structs; -use crate::{descriptor_sets, TypesMeta}; +use crate::TypesMeta; fn include_callback( requested_source_path_raw: &str, @@ -247,14 +247,15 @@ pub(super) fn reflect( let mut entry_points_outside_impl: Vec = vec![]; for instruction in doc.instructions.iter() { if let &Instruction::EntryPoint { .. } = instruction { - let (outside, entry_point) = entry_point::write_entry_point(&doc, instruction); + let (outside, entry_point, descriptor_sets) = + entry_point::write_entry_point(&doc, instruction, &types_meta); entry_points_inside_impl.push(entry_point); entry_points_outside_impl.push(outside); + entry_points_outside_impl.push(descriptor_sets); } } let structs = structs::write_structs(&doc, &types_meta); - let descriptor_sets = descriptor_sets::write_descriptor_sets(&doc, &types_meta); let specialization_constants = spec_consts::write_specialization_constants(&doc, &types_meta); let uses = &types_meta.uses; let ast = quote! { @@ -334,7 +335,6 @@ pub(super) fn reflect( #structs } - #descriptor_sets #specialization_constants }; diff --git a/vulkano-shaders/src/descriptor_sets.rs b/vulkano-shaders/src/descriptor_sets.rs index 2ebde565..8115b0d8 100644 --- a/vulkano-shaders/src/descriptor_sets.rs +++ b/vulkano-shaders/src/descriptor_sets.rs @@ -9,55 +9,32 @@ use std::cmp; -use proc_macro2::TokenStream; +use proc_macro2::{Ident, TokenStream}; use crate::enums::{Decoration, Dim, ImageFormat, StorageClass}; use crate::parse::{Instruction, Spirv}; use crate::{spirv_search, TypesMeta}; +use std::collections::HashSet; -pub(super) fn write_descriptor_sets(doc: &Spirv, types_meta: &TypesMeta) -> TokenStream { - // TODO: not implemented correctly +struct Descriptor { + set: u32, + binding: u32, + desc_ty: TokenStream, + array_count: u64, + readonly: bool, +} + +pub(super) fn write_descriptor_sets( + doc: &Spirv, + entry_point_layout_name: &Ident, + entrypoint_id: u32, + interface: &[u32], + types_meta: &TypesMeta, +) -> TokenStream { + // TODO: somewhat implemented correctly // Finding all the descriptors. - let mut descriptors = Vec::new(); - struct Descriptor { - set: u32, - binding: u32, - desc_ty: TokenStream, - array_count: u64, - readonly: bool, - } - - // Looping to find all the elements that have the `DescriptorSet` decoration. - for set_decoration in doc.get_decorations(Decoration::DecorationDescriptorSet) { - let variable_id = set_decoration.target_id; - let set = set_decoration.params[0]; - - // Find which type is pointed to by this variable. - let (pointed_ty, storage_class) = pointer_variable_ty(doc, variable_id); - // Name of the variable. - let name = spirv_search::name_from_id(doc, variable_id); - - // Find the binding point of this descriptor. - // TODO: There was a previous todo here, I think it was asking for this to be implemented for member decorations? check git history - let binding = doc - .get_decoration_params(variable_id, Decoration::DecorationBinding) - .unwrap()[0]; - - // Find information about the kind of binding for this descriptor. - let (desc_ty, readonly, array_count) = - descriptor_infos(doc, pointed_ty, storage_class, false).expect(&format!( - "Couldn't find relevant type for uniform `{}` (type {}, maybe unimplemented)", - name, pointed_ty - )); - descriptors.push(Descriptor { - desc_ty, - set, - binding, - array_count, - readonly, - }); - } + let descriptors = find_descriptors(doc, entrypoint_id, interface); // Looping to find all the push constant structs. let mut push_constants_size = 0; @@ -127,10 +104,10 @@ pub(super) fn write_descriptor_sets(doc: &Spirv, types_meta: &TypesMeta) -> Toke quote! { #[derive(Debug, Clone)] - pub struct Layout(pub ShaderStages); + pub struct #entry_point_layout_name(pub ShaderStages); #[allow(unsafe_code)] - unsafe impl PipelineLayoutDesc for Layout { + unsafe impl PipelineLayoutDesc for #entry_point_layout_name { fn num_sets(&self) -> usize { #num_sets } @@ -160,6 +137,201 @@ pub(super) fn write_descriptor_sets(doc: &Spirv, types_meta: &TypesMeta) -> Toke } } +fn find_descriptors(doc: &Spirv, entrypoint_id: u32, interface: &[u32]) -> Vec { + let mut descriptors = Vec::new(); + + // 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, + // 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. + let variables = { + let mut found_variables: HashSet = interface.iter().cloned().collect(); + let mut inspected_functions: HashSet = HashSet::new(); + find_variables_in_function( + &doc, + entrypoint_id, + &mut inspected_functions, + &mut found_variables, + ); + found_variables + }; + + // Looping to find all the interface elements that have the `DescriptorSet` decoration. + for set_decoration in doc.get_decorations(Decoration::DecorationDescriptorSet) { + let variable_id = set_decoration.target_id; + + if !variables.contains(&variable_id) { + continue; + } + + let set = set_decoration.params[0]; + + // Find which type is pointed to by this variable. + let (pointed_ty, storage_class) = pointer_variable_ty(doc, variable_id); + // Name of the variable. + let name = spirv_search::name_from_id(doc, variable_id); + + // Find the binding point of this descriptor. + // TODO: There was a previous todo here, I think it was asking for this to be implemented for member decorations? check git history + let binding = doc + .get_decoration_params(variable_id, Decoration::DecorationBinding) + .unwrap()[0]; + + // Find information about the kind of binding for this descriptor. + let (desc_ty, readonly, array_count) = + descriptor_infos(doc, pointed_ty, storage_class, false).expect(&format!( + "Couldn't find relevant type for uniform `{}` (type {}, maybe unimplemented)", + name, pointed_ty + )); + descriptors.push(Descriptor { + desc_ty, + set, + binding, + array_count, + readonly, + }); + } + + descriptors +} + +// Recursively finds every pointer variable used in the execution of a function. +fn find_variables_in_function( + doc: &Spirv, + function: u32, + inspected_functions: &mut HashSet, + found_variables: &mut HashSet, +) { + inspected_functions.insert(function); + let mut in_function = false; + for instruction in &doc.instructions { + if !in_function { + match instruction { + Instruction::Function { result_id, .. } if result_id == &function => { + in_function = true; + } + _ => {} + } + } else { + // We only care about instructions that accept pointers. + // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_universal_validation_rules + match instruction { + Instruction::Load { pointer, .. } | Instruction::Store { pointer, .. } => { + found_variables.insert(*pointer); + } + Instruction::AccessChain { base_id, .. } + | Instruction::InBoundsAccessChain { base_id, .. } => { + found_variables.insert(*base_id); + } + Instruction::FunctionCall { + function_id, args, .. + } => { + args.iter().for_each(|&x| { + found_variables.insert(x); + }); + if !inspected_functions.contains(function_id) { + find_variables_in_function( + doc, + *function_id, + inspected_functions, + found_variables, + ); + } + } + Instruction::ImageTexelPointer { + image, + coordinate, + sample, + .. + } => { + found_variables.insert(*image); + found_variables.insert(*coordinate); + found_variables.insert(*sample); + } + Instruction::CopyMemory { + target_id, + source_id, + .. + } => { + found_variables.insert(*target_id); + found_variables.insert(*source_id); + } + Instruction::CopyObject { operand_id, .. } => { + found_variables.insert(*operand_id); + } + Instruction::AtomicLoad { pointer, .. } + | Instruction::AtomicIIncrement { pointer, .. } + | Instruction::AtomicIDecrement { pointer, .. } + | Instruction::AtomicFlagTestAndSet { pointer, .. } + | Instruction::AtomicFlagClear { pointer, .. } => { + found_variables.insert(*pointer); + } + Instruction::AtomicStore { + pointer, value_id, .. + } + | Instruction::AtomicExchange { + pointer, value_id, .. + } + | Instruction::AtomicIAdd { + pointer, value_id, .. + } + | Instruction::AtomicISub { + pointer, value_id, .. + } + | Instruction::AtomicSMin { + pointer, value_id, .. + } + | Instruction::AtomicUMin { + pointer, value_id, .. + } + | Instruction::AtomicSMax { + pointer, value_id, .. + } + | Instruction::AtomicUMax { + pointer, value_id, .. + } + | Instruction::AtomicAnd { + pointer, value_id, .. + } + | Instruction::AtomicOr { + pointer, value_id, .. + } + | Instruction::AtomicXor { + pointer, value_id, .. + } => { + found_variables.insert(*pointer); + found_variables.insert(*value_id); + } + Instruction::AtomicCompareExchange { + pointer, + value_id, + comparator_id, + .. + } + | Instruction::AtomicCompareExchangeWeak { + pointer, + value_id, + comparator_id, + .. + } => { + found_variables.insert(*pointer); + found_variables.insert(*value_id); + found_variables.insert(*comparator_id); + } + Instruction::ExtInst { operands, .. } => { + // We don't know which extended instructions take pointers, + // so we must interpret every operand as a pointer. + operands.iter().for_each(|&o| { + found_variables.insert(o); + }); + } + Instruction::FunctionEnd => return, + _ => {} + } + } + } +} + /// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed /// type and the storage class. fn pointer_variable_ty(doc: &Spirv, variable: u32) -> (u32, StorageClass) { @@ -210,9 +382,12 @@ fn descriptor_infos( let decoration_block = doc .get_decoration_params(pointed_ty, Decoration::DecorationBlock) .is_some(); + let decoration_buffer_block = doc + .get_decoration_params(pointed_ty, Decoration::DecorationBufferBlock) + .is_some(); assert!( - decoration_block, - "Structs in shader interface are expected to be decorated with Block" + decoration_block ^ decoration_buffer_block, + "Structs in shader interface are expected to be decorated with one of Block or BufferBlock" ); let is_ssbo = pointer_storage == StorageClass::StorageClassStorageBuffer; @@ -366,3 +541,183 @@ fn descriptor_infos( }) .next() } + +#[cfg(test)] +mod tests { + use super::*; + use crate::codegen::compile; + use crate::parse; + use shaderc::ShaderKind; + use std::path::{Path, PathBuf}; + + /// `entrypoint1.frag.glsl`: + /// ```glsl + /// #version 450 + /// + /// layout(set = 0, binding = 0) uniform Uniform { + /// uint data; + /// } ubo; + /// + /// layout(set = 0, binding = 1) buffer Buffer { + /// uint data; + /// } bo; + /// + /// layout(set = 0, binding = 2) uniform sampler textureSampler; + /// layout(set = 0, binding = 3) uniform texture2D imageTexture; + /// + /// layout(push_constant) uniform PushConstant { + /// uint data; + /// } push; + /// + /// layout(input_attachment_index = 0, set = 0, binding = 4) uniform subpassInput inputAttachment; + /// + /// layout(location = 0) out vec4 outColor; + /// + /// void entrypoint1() { + /// bo.data = 12; + /// outColor = vec4( + /// float(ubo.data), + /// float(push.data), + /// texture(sampler2D(imageTexture, textureSampler), vec2(0.0, 0.0)).x, + /// subpassLoad(inputAttachment).x + /// ); + /// } + /// ``` + /// + /// `entrypoint2.frag.glsl`: + /// ```glsl + /// #version 450 + /// + /// layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput inputAttachment2; + /// + /// layout(set = 0, binding = 1) buffer Buffer { + /// uint data; + /// } bo2; + /// + /// layout(set = 0, binding = 2) uniform Uniform { + /// uint data; + /// } ubo2; + /// + /// layout(push_constant) uniform PushConstant { + /// uint data; + /// } push2; + /// + /// void entrypoint2() { + /// bo2.data = ubo2.data + push2.data + int(subpassLoad(inputAttachment2).y); + /// } + /// ``` + /// + /// Compiled and linked with: + /// ```sh + /// glslangvalidator -e entrypoint1 --source-entrypoint entrypoint1 -V100 entrypoint1.frag.glsl -o entrypoint1.spv + /// glslangvalidator -e entrypoint2 --source-entrypoint entrypoint2 -V100 entrypoint2.frag.glsl -o entrypoint2.spv + /// spirv-link entrypoint1.spv entrypoint2.spv -o multiple_entrypoints.spv + /// ``` + #[test] + fn test_descriptor_calculation_with_multiple_entrypoints() { + let data = include_bytes!("../tests/multiple_entrypoints.spv"); + let instructions: Vec = data + .chunks(4) + .map(|c| { + ((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32 + }) + .collect(); + let doc = parse::parse_spirv(&instructions).unwrap(); + + let mut descriptors = Vec::new(); + for instruction in doc.instructions.iter() { + if let &Instruction::EntryPoint { + id, ref interface, .. + } = instruction + { + descriptors.push(find_descriptors(&doc, id, interface)); + } + } + + // Check first entrypoint + let e1_descriptors = descriptors.get(0).expect("Could not find entrypoint1"); + let mut e1_bindings = Vec::new(); + for d in e1_descriptors { + e1_bindings.push((d.set, d.binding)); + } + assert_eq!(e1_bindings.len(), 5); + assert!(e1_bindings.contains(&(0, 0))); + assert!(e1_bindings.contains(&(0, 1))); + assert!(e1_bindings.contains(&(0, 2))); + assert!(e1_bindings.contains(&(0, 3))); + assert!(e1_bindings.contains(&(0, 4))); + + // Check second entrypoint + let e2_descriptors = descriptors.get(1).expect("Could not find entrypoint2"); + let mut e2_bindings = Vec::new(); + for d in e2_descriptors { + e2_bindings.push((d.set, d.binding)); + } + assert_eq!(e2_bindings.len(), 3); + assert!(e2_bindings.contains(&(0, 0))); + assert!(e2_bindings.contains(&(0, 1))); + assert!(e2_bindings.contains(&(0, 2))); + } + + #[test] + fn test_descriptor_calculation_with_multiple_functions() { + let includes: [PathBuf; 0] = []; + let defines: [(String, String); 0] = []; + let comp = compile( + None, + &Path::new(""), + " + #version 450 + + layout(set = 1, binding = 0) buffer Buffer { + vec3 data; + } bo; + + layout(set = 2, binding = 0) uniform Uniform { + float data; + } ubo; + + layout(set = 3, binding = 1) uniform sampler textureSampler; + layout(set = 3, binding = 2) uniform texture2D imageTexture; + + float withMagicSparkles(float data) { + return texture(sampler2D(imageTexture, textureSampler), vec2(data, data)).x; + } + + vec3 makeSecretSauce() { + return vec3(withMagicSparkles(ubo.data)); + } + + void main() { + bo.data = makeSecretSauce(); + } + ", + ShaderKind::Vertex, + &includes, + &defines, + ) + .unwrap(); + let doc = parse::parse_spirv(comp.as_binary()).unwrap(); + + for instruction in doc.instructions.iter() { + if let &Instruction::EntryPoint { + id, ref interface, .. + } = instruction + { + let descriptors = find_descriptors(&doc, id, interface); + let mut bindings = Vec::new(); + for d in descriptors { + bindings.push((d.set, d.binding)); + } + assert_eq!(bindings.len(), 4); + assert!(bindings.contains(&(1, 0))); + assert!(bindings.contains(&(2, 0))); + assert!(bindings.contains(&(3, 1))); + assert!(bindings.contains(&(3, 2))); + + return; + } + } + panic!("Could not find entrypoint"); + } +} diff --git a/vulkano-shaders/src/entry_point.rs b/vulkano-shaders/src/entry_point.rs index c83fcf7c..ba397241 100644 --- a/vulkano-shaders/src/entry_point.rs +++ b/vulkano-shaders/src/entry_point.rs @@ -10,11 +10,16 @@ use proc_macro2::{Span, TokenStream}; use syn::Ident; +use crate::descriptor_sets::write_descriptor_sets; use crate::enums::{Decoration, ExecutionMode, ExecutionModel, StorageClass}; use crate::parse::{Instruction, Spirv}; -use crate::spirv_search; +use crate::{spirv_search, TypesMeta}; -pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream, TokenStream) { +pub(super) fn write_entry_point( + doc: &Spirv, + instruction: &Instruction, + types_meta: &TypesMeta, +) -> (TokenStream, TokenStream, TokenStream) { let (execution, id, ep_name, interface) = match instruction { &Instruction::EntryPoint { ref execution, @@ -52,6 +57,18 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream ignore_first_array_out, ); + let descriptor_sets_layout_name = Ident::new( + format!("{}Layout", capitalized_ep_name).as_str(), + Span::call_site(), + ); + let descriptor_sets_layout_struct = write_descriptor_sets( + &doc, + &descriptor_sets_layout_name, + id, + interface, + &types_meta, + ); + let spec_consts_struct = if crate::spec_consts::has_specialization_constants(doc) { quote! { SpecializationConstants } } else { @@ -61,10 +78,10 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream let (ty, f_call) = { if let ExecutionModel::ExecutionModelGLCompute = *execution { ( - quote! { ::vulkano::pipeline::shader::ComputeEntryPoint<#spec_consts_struct, Layout> }, + quote! { ::vulkano::pipeline::shader::ComputeEntryPoint<#spec_consts_struct, #descriptor_sets_layout_name> }, quote! { compute_entry_point( ::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _), - Layout(ShaderStages { compute: true, .. ShaderStages::none() }) + #descriptor_sets_layout_name(ShaderStages { compute: true, .. ShaderStages::none() }) )}, ) } else { @@ -171,14 +188,14 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream #spec_consts_struct, #capitalized_ep_name_input, #capitalized_ep_name_output, - Layout> + #descriptor_sets_layout_name> }; let f_call = quote! { graphics_entry_point( ::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _), #capitalized_ep_name_input, #capitalized_ep_name_output, - Layout(#stage), + #descriptor_sets_layout_name(#stage), #entry_ty ) }; @@ -207,7 +224,11 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream } }; - (interface_structs, entry_point) + ( + interface_structs, + entry_point, + descriptor_sets_layout_struct, + ) } struct Element { diff --git a/vulkano-shaders/src/parse.rs b/vulkano-shaders/src/parse.rs index d73d0fdb..dae6b908 100644 --- a/vulkano-shaders/src/parse.rs +++ b/vulkano-shaders/src/parse.rs @@ -75,6 +75,13 @@ pub enum Instruction { result_id: u32, name: String, }, + ExtInst { + result_type_id: u32, + result_id: u32, + set: u32, + instruction: u32, + operands: Vec, + }, MemoryModel(AddressingModel, MemoryModel), EntryPoint { execution: ExecutionModel, @@ -176,13 +183,61 @@ pub enum Instruction { result_id: u32, data: Vec, }, + Function { + result_type_id: u32, + result_id: u32, + function_control: u32, + function_type_id: u32, + }, FunctionEnd, + FunctionCall { + result_type_id: u32, + result_id: u32, + function_id: u32, + args: Vec, + }, Variable { result_type_id: u32, result_id: u32, storage_class: StorageClass, initializer: Option, }, + ImageTexelPointer { + result_type_id: u32, + result_id: u32, + image: u32, + coordinate: u32, + sample: u32, + }, + Load { + result_type_id: u32, + result_id: u32, + pointer: u32, + memory_operands: Option, + }, + Store { + pointer: u32, + object: u32, + memory_operands: Option, + }, + CopyMemory { + target_id: u32, + source_id: u32, + memory_operands: Option, + source_memory_operands: Option, + }, + AccessChain { + result_type_id: u32, + result_id: u32, + base_id: u32, + indexes: Vec, + }, + InBoundsAccessChain { + result_type_id: u32, + result_id: u32, + base_id: u32, + indexes: Vec, + }, Decorate { target_id: u32, decoration: Decoration, @@ -205,6 +260,138 @@ pub enum Instruction { decoration_group: u32, targets: Vec<(u32, u32)>, }, + CopyObject { + result_type_id: u32, + result_id: u32, + operand_id: u32, + }, + AtomicLoad { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + }, + AtomicStore { + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, + AtomicExchange { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, + AtomicCompareExchange { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_equal_id: u32, + memory_semantics_unequal_id: u32, + value_id: u32, + comparator_id: u32, + }, + AtomicCompareExchangeWeak { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_equal_id: u32, + memory_semantics_unequal_id: u32, + value_id: u32, + comparator_id: u32, + }, + AtomicIIncrement { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + }, + AtomicIDecrement { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + }, + AtomicIAdd { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, + AtomicISub { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, + AtomicSMin { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, + AtomicUMin { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, + AtomicSMax { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, + AtomicUMax { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, + AtomicAnd { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, + AtomicOr { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, + AtomicXor { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + value_id: u32, + }, Label { result_id: u32, }, @@ -213,6 +400,18 @@ pub enum Instruction { }, Kill, Return, + AtomicFlagTestAndSet { + result_type_id: u32, + result_id: u32, + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + }, + AtomicFlagClear { + pointer: u32, + scope_id: u32, + memory_semantics_id: u32, + }, } fn parse_instruction(i: &[u32]) -> Result<(Instruction, &[u32]), ParseError> { @@ -246,6 +445,13 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result Instruction::ExtInst { + result_type_id: operands[0], + result_id: operands[1], + set: operands[2], + instruction: operands[3], + operands: operands[4..].to_owned(), + }, 14 => Instruction::MemoryModel( AddressingModel::from_num(operands[0])?, MemoryModel::from_num(operands[1])?, @@ -367,13 +573,61 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result Instruction::Function { + result_type_id: operands[0], + result_id: operands[1], + function_control: operands[2], + function_type_id: operands[3], + }, 56 => Instruction::FunctionEnd, + 57 => Instruction::FunctionCall { + result_type_id: operands[0], + result_id: operands[1], + function_id: operands[2], + args: operands[3..].to_owned(), + }, 59 => Instruction::Variable { result_type_id: operands[0], result_id: operands[1], storage_class: StorageClass::from_num(operands[2])?, initializer: operands.get(3).map(|&v| v), }, + 60 => Instruction::ImageTexelPointer { + result_type_id: operands[0], + result_id: operands[1], + image: operands[2], + coordinate: operands[3], + sample: operands[4], + }, + 61 => Instruction::Load { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + memory_operands: operands.get(3).map(|&v| v), + }, + 62 => Instruction::Store { + pointer: operands[0], + object: operands[1], + memory_operands: operands.get(2).map(|&v| v), + }, + 63 => Instruction::CopyMemory { + target_id: operands[0], + source_id: operands[1], + memory_operands: operands.get(2).map(|&v| v), + source_memory_operands: operands.get(3).map(|&v| v), + }, + 65 => Instruction::AccessChain { + result_type_id: operands[0], + result_id: operands[1], + base_id: operands[2], + indexes: operands[3..].iter().map(|&v| v as i32).collect(), + }, + 66 => Instruction::InBoundsAccessChain { + result_type_id: operands[0], + result_id: operands[1], + base_id: operands[2], + indexes: operands[3..].iter().map(|&v| v as i32).collect(), + }, 71 => Instruction::Decorate { target_id: operands[0], decoration: Decoration::from_num(operands[1])?, @@ -396,6 +650,138 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result Instruction::CopyObject { + result_type_id: operands[0], + result_id: operands[1], + operand_id: operands[2], + }, + 227 => Instruction::AtomicLoad { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + }, + 228 => Instruction::AtomicStore { + pointer: operands[0], + scope_id: operands[1], + memory_semantics_id: operands[2], + value_id: operands[3], + }, + 229 => Instruction::AtomicExchange { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + value_id: operands[5], + }, + 230 => Instruction::AtomicCompareExchange { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_equal_id: operands[4], + memory_semantics_unequal_id: operands[5], + value_id: operands[6], + comparator_id: operands[7], + }, + 231 => Instruction::AtomicCompareExchangeWeak { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_equal_id: operands[4], + memory_semantics_unequal_id: operands[5], + value_id: operands[6], + comparator_id: operands[7], + }, + 232 => Instruction::AtomicIIncrement { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + }, + 233 => Instruction::AtomicIDecrement { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + }, + 234 => Instruction::AtomicIAdd { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + value_id: operands[5], + }, + 235 => Instruction::AtomicISub { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + value_id: operands[5], + }, + 236 => Instruction::AtomicSMin { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + value_id: operands[5], + }, + 237 => Instruction::AtomicUMin { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + value_id: operands[5], + }, + 238 => Instruction::AtomicSMax { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + value_id: operands[5], + }, + 239 => Instruction::AtomicUMax { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + value_id: operands[5], + }, + 240 => Instruction::AtomicAnd { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + value_id: operands[5], + }, + 241 => Instruction::AtomicOr { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + value_id: operands[5], + }, + 242 => Instruction::AtomicXor { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + value_id: operands[5], + }, 248 => Instruction::Label { result_id: operands[0], }, @@ -404,6 +790,18 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result Instruction::Kill, 253 => Instruction::Return, + 318 => Instruction::AtomicFlagTestAndSet { + result_type_id: operands[0], + result_id: operands[1], + pointer: operands[2], + scope_id: operands[3], + memory_semantics_id: operands[4], + }, + 319 => Instruction::AtomicFlagClear { + pointer: operands[0], + scope_id: operands[1], + memory_semantics_id: operands[2], + }, _ => Instruction::Unknown(opcode, operands.to_owned()), }) } diff --git a/vulkano-shaders/tests/multiple_entrypoints.spv b/vulkano-shaders/tests/multiple_entrypoints.spv new file mode 100644 index 0000000000000000000000000000000000000000..3d700b581ebaf1d39bfb17e77d80380349124d8c GIT binary patch literal 2188 zcmZXUYfn=_5Qevx11icz1O*fectJq~1Vj-KP>YI*1}~pmX@!LLH03nW#1H;9f0bWM ze4jmMbk#|wGxN^u?Ci{(7V=Zw&gI;IbMBQG8gO|bshpPVab4an@9po+og@cyOUv`b z^|}JW`}$nBD@xCcYt5wnr4=7FlLa5qr}OyWKfXoRFE8;b!0Y(Y3mN`hMTC0fqW|(o z${Oj+cBfviwViGvDy4%;QbCK9rAx9JqhmQ2caoj>IMxZUqQCcD^Qa!T8$JhhKw*wk zvO7vb{AuZ}&dDJ$kxrbEJZe-v)!x=VC!Kc9@E$2i3hh@Ktz&8={w(p$Rwvm?lFG-! zhWa9$%zyWYYR?I)#*5zDD_u1#N_e|Zk~#n)y`o?8pS(VHS6=JOlwzQ=a)Z&2vTmkF zlVZs13F+(tHgV%(h=`kHO&`N}Y~pj7pWcRk?C?H%8~U;FL*&P{`vz4d-_(R&b6ygCNHK$wB{4KEAN^0U zqPrkHF9usvEH*O%4!a^orOq zN%{`X-DamVFZfwootG6uUS>@!HnRmI*A+>=5A&N1hMyfmC*O#)t6rw>lKPu+nZ}t? zrW4DZ*z5~5s+%VyiJSCDIv3)w*&Ap~FJxEAK@YA;(gW(If9Ti6(5$9_|ArV0wjSS< z4pIBGm+725nEj9wqNZD^>SL)1n;AjWaa)qva)x`c*&}8PbB0}DU#(W+XT=bHX28DU z$EN4}H_&Ihll_G3jyqmY^Vr+wgyqC5s)L=icd)}hWZjkAl8@b*7qg$#xS`%+vw!Aa zkj`GN$#(-``;BWB)V%##fNrrPQKQ!(ej?S0Rr zvlsThvUD(Z!``