mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-21 22:34:43 +00:00
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>
This commit is contained in:
parent
b39086182b
commit
73eb8e7a9f
@ -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.
|
||||
|
@ -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<TokenStream> = 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
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
||||
// Finding all the descriptors.
|
||||
let mut descriptors = Vec::new();
|
||||
struct Descriptor {
|
||||
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];
|
||||
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
|
||||
|
||||
// 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,
|
||||
});
|
||||
}
|
||||
// Finding all the descriptors.
|
||||
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<Descriptor> {
|
||||
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<u32> = interface.iter().cloned().collect();
|
||||
let mut inspected_functions: HashSet<u32> = 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<u32>,
|
||||
found_variables: &mut HashSet<u32>,
|
||||
) {
|
||||
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<u32> = 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");
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<u32>,
|
||||
},
|
||||
MemoryModel(AddressingModel, MemoryModel),
|
||||
EntryPoint {
|
||||
execution: ExecutionModel,
|
||||
@ -176,13 +183,61 @@ pub enum Instruction {
|
||||
result_id: u32,
|
||||
data: Vec<u32>,
|
||||
},
|
||||
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<u32>,
|
||||
},
|
||||
Variable {
|
||||
result_type_id: u32,
|
||||
result_id: u32,
|
||||
storage_class: StorageClass,
|
||||
initializer: Option<u32>,
|
||||
},
|
||||
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<u32>,
|
||||
},
|
||||
Store {
|
||||
pointer: u32,
|
||||
object: u32,
|
||||
memory_operands: Option<u32>,
|
||||
},
|
||||
CopyMemory {
|
||||
target_id: u32,
|
||||
source_id: u32,
|
||||
memory_operands: Option<u32>,
|
||||
source_memory_operands: Option<u32>,
|
||||
},
|
||||
AccessChain {
|
||||
result_type_id: u32,
|
||||
result_id: u32,
|
||||
base_id: u32,
|
||||
indexes: Vec<i32>,
|
||||
},
|
||||
InBoundsAccessChain {
|
||||
result_type_id: u32,
|
||||
result_id: u32,
|
||||
base_id: u32,
|
||||
indexes: Vec<i32>,
|
||||
},
|
||||
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, Pars
|
||||
result_id: operands[0],
|
||||
name: parse_string(&operands[1..]).0,
|
||||
},
|
||||
12 => 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, Pars
|
||||
result_id: operands[1],
|
||||
data: operands[2..].to_owned(),
|
||||
},
|
||||
54 => 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, Pars
|
||||
decoration_group: operands[0],
|
||||
targets: operands.chunks(2).map(|x| (x[0], x[1])).collect(),
|
||||
},
|
||||
83 => 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, Pars
|
||||
},
|
||||
252 => 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()),
|
||||
})
|
||||
}
|
||||
|
BIN
vulkano-shaders/tests/multiple_entrypoints.spv
Normal file
BIN
vulkano-shaders/tests/multiple_entrypoints.spv
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user