mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-22 06:45:23 +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`, 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** On `AutoCommandBufferBuilder` and `SyncCommandBufferBuilder`, the `update_buffer` method now takes `data` by reference
|
||||||
- **Breaking** Made `PipelineLayoutDescTweaks` public, for use with compute pipelines
|
- **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 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.
|
- 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.
|
- 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::read_file_to_string;
|
||||||
use crate::spec_consts;
|
use crate::spec_consts;
|
||||||
use crate::structs;
|
use crate::structs;
|
||||||
use crate::{descriptor_sets, TypesMeta};
|
use crate::TypesMeta;
|
||||||
|
|
||||||
fn include_callback(
|
fn include_callback(
|
||||||
requested_source_path_raw: &str,
|
requested_source_path_raw: &str,
|
||||||
@ -247,14 +247,15 @@ pub(super) fn reflect(
|
|||||||
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) = 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_inside_impl.push(entry_point);
|
||||||
entry_points_outside_impl.push(outside);
|
entry_points_outside_impl.push(outside);
|
||||||
|
entry_points_outside_impl.push(descriptor_sets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let structs = structs::write_structs(&doc, &types_meta);
|
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 specialization_constants = spec_consts::write_specialization_constants(&doc, &types_meta);
|
||||||
let uses = &types_meta.uses;
|
let uses = &types_meta.uses;
|
||||||
let ast = quote! {
|
let ast = quote! {
|
||||||
@ -334,7 +335,6 @@ pub(super) fn reflect(
|
|||||||
#structs
|
#structs
|
||||||
}
|
}
|
||||||
|
|
||||||
#descriptor_sets
|
|
||||||
#specialization_constants
|
#specialization_constants
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,55 +9,32 @@
|
|||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::{Ident, TokenStream};
|
||||||
|
|
||||||
use crate::enums::{Decoration, Dim, ImageFormat, StorageClass};
|
use crate::enums::{Decoration, Dim, ImageFormat, StorageClass};
|
||||||
use crate::parse::{Instruction, Spirv};
|
use crate::parse::{Instruction, Spirv};
|
||||||
use crate::{spirv_search, TypesMeta};
|
use crate::{spirv_search, TypesMeta};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
pub(super) fn write_descriptor_sets(doc: &Spirv, types_meta: &TypesMeta) -> TokenStream {
|
struct Descriptor {
|
||||||
// TODO: not implemented correctly
|
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.
|
// Finding all the descriptors.
|
||||||
let mut descriptors = Vec::new();
|
let descriptors = find_descriptors(doc, entrypoint_id, interface);
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
||||||
@ -127,10 +104,10 @@ pub(super) fn write_descriptor_sets(doc: &Spirv, types_meta: &TypesMeta) -> Toke
|
|||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Layout(pub ShaderStages);
|
pub struct #entry_point_layout_name(pub ShaderStages);
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe impl PipelineLayoutDesc for Layout {
|
unsafe impl PipelineLayoutDesc for #entry_point_layout_name {
|
||||||
fn num_sets(&self) -> usize {
|
fn num_sets(&self) -> usize {
|
||||||
#num_sets
|
#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
|
/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
|
||||||
/// type and the storage class.
|
/// type and the storage class.
|
||||||
fn pointer_variable_ty(doc: &Spirv, variable: u32) -> (u32, StorageClass) {
|
fn pointer_variable_ty(doc: &Spirv, variable: u32) -> (u32, StorageClass) {
|
||||||
@ -210,9 +382,12 @@ fn descriptor_infos(
|
|||||||
let decoration_block = doc
|
let decoration_block = doc
|
||||||
.get_decoration_params(pointed_ty, Decoration::DecorationBlock)
|
.get_decoration_params(pointed_ty, Decoration::DecorationBlock)
|
||||||
.is_some();
|
.is_some();
|
||||||
|
let decoration_buffer_block = doc
|
||||||
|
.get_decoration_params(pointed_ty, Decoration::DecorationBufferBlock)
|
||||||
|
.is_some();
|
||||||
assert!(
|
assert!(
|
||||||
decoration_block,
|
decoration_block ^ decoration_buffer_block,
|
||||||
"Structs in shader interface are expected to be decorated with Block"
|
"Structs in shader interface are expected to be decorated with one of Block or BufferBlock"
|
||||||
);
|
);
|
||||||
let is_ssbo = pointer_storage == StorageClass::StorageClassStorageBuffer;
|
let is_ssbo = pointer_storage == StorageClass::StorageClassStorageBuffer;
|
||||||
|
|
||||||
@ -366,3 +541,183 @@ fn descriptor_infos(
|
|||||||
})
|
})
|
||||||
.next()
|
.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 proc_macro2::{Span, TokenStream};
|
||||||
use syn::Ident;
|
use syn::Ident;
|
||||||
|
|
||||||
|
use crate::descriptor_sets::write_descriptor_sets;
|
||||||
use crate::enums::{Decoration, ExecutionMode, ExecutionModel, StorageClass};
|
use crate::enums::{Decoration, ExecutionMode, ExecutionModel, StorageClass};
|
||||||
use crate::parse::{Instruction, Spirv};
|
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 {
|
let (execution, id, ep_name, interface) = match instruction {
|
||||||
&Instruction::EntryPoint {
|
&Instruction::EntryPoint {
|
||||||
ref execution,
|
ref execution,
|
||||||
@ -52,6 +57,18 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream
|
|||||||
ignore_first_array_out,
|
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) {
|
let spec_consts_struct = if crate::spec_consts::has_specialization_constants(doc) {
|
||||||
quote! { SpecializationConstants }
|
quote! { SpecializationConstants }
|
||||||
} else {
|
} else {
|
||||||
@ -61,10 +78,10 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream
|
|||||||
let (ty, f_call) = {
|
let (ty, f_call) = {
|
||||||
if let ExecutionModel::ExecutionModelGLCompute = *execution {
|
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(
|
quote! { compute_entry_point(
|
||||||
::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _),
|
::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 {
|
} else {
|
||||||
@ -171,14 +188,14 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream
|
|||||||
#spec_consts_struct,
|
#spec_consts_struct,
|
||||||
#capitalized_ep_name_input,
|
#capitalized_ep_name_input,
|
||||||
#capitalized_ep_name_output,
|
#capitalized_ep_name_output,
|
||||||
Layout>
|
#descriptor_sets_layout_name>
|
||||||
};
|
};
|
||||||
let f_call = quote! {
|
let f_call = quote! {
|
||||||
graphics_entry_point(
|
graphics_entry_point(
|
||||||
::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _),
|
::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _),
|
||||||
#capitalized_ep_name_input,
|
#capitalized_ep_name_input,
|
||||||
#capitalized_ep_name_output,
|
#capitalized_ep_name_output,
|
||||||
Layout(#stage),
|
#descriptor_sets_layout_name(#stage),
|
||||||
#entry_ty
|
#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 {
|
struct Element {
|
||||||
|
@ -75,6 +75,13 @@ pub enum Instruction {
|
|||||||
result_id: u32,
|
result_id: u32,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
ExtInst {
|
||||||
|
result_type_id: u32,
|
||||||
|
result_id: u32,
|
||||||
|
set: u32,
|
||||||
|
instruction: u32,
|
||||||
|
operands: Vec<u32>,
|
||||||
|
},
|
||||||
MemoryModel(AddressingModel, MemoryModel),
|
MemoryModel(AddressingModel, MemoryModel),
|
||||||
EntryPoint {
|
EntryPoint {
|
||||||
execution: ExecutionModel,
|
execution: ExecutionModel,
|
||||||
@ -176,13 +183,61 @@ pub enum Instruction {
|
|||||||
result_id: u32,
|
result_id: u32,
|
||||||
data: Vec<u32>,
|
data: Vec<u32>,
|
||||||
},
|
},
|
||||||
|
Function {
|
||||||
|
result_type_id: u32,
|
||||||
|
result_id: u32,
|
||||||
|
function_control: u32,
|
||||||
|
function_type_id: u32,
|
||||||
|
},
|
||||||
FunctionEnd,
|
FunctionEnd,
|
||||||
|
FunctionCall {
|
||||||
|
result_type_id: u32,
|
||||||
|
result_id: u32,
|
||||||
|
function_id: u32,
|
||||||
|
args: Vec<u32>,
|
||||||
|
},
|
||||||
Variable {
|
Variable {
|
||||||
result_type_id: u32,
|
result_type_id: u32,
|
||||||
result_id: u32,
|
result_id: u32,
|
||||||
storage_class: StorageClass,
|
storage_class: StorageClass,
|
||||||
initializer: Option<u32>,
|
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 {
|
Decorate {
|
||||||
target_id: u32,
|
target_id: u32,
|
||||||
decoration: Decoration,
|
decoration: Decoration,
|
||||||
@ -205,6 +260,138 @@ pub enum Instruction {
|
|||||||
decoration_group: u32,
|
decoration_group: u32,
|
||||||
targets: Vec<(u32, 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 {
|
Label {
|
||||||
result_id: u32,
|
result_id: u32,
|
||||||
},
|
},
|
||||||
@ -213,6 +400,18 @@ pub enum Instruction {
|
|||||||
},
|
},
|
||||||
Kill,
|
Kill,
|
||||||
Return,
|
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> {
|
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],
|
result_id: operands[0],
|
||||||
name: parse_string(&operands[1..]).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(
|
14 => Instruction::MemoryModel(
|
||||||
AddressingModel::from_num(operands[0])?,
|
AddressingModel::from_num(operands[0])?,
|
||||||
MemoryModel::from_num(operands[1])?,
|
MemoryModel::from_num(operands[1])?,
|
||||||
@ -367,13 +573,61 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, Pars
|
|||||||
result_id: operands[1],
|
result_id: operands[1],
|
||||||
data: operands[2..].to_owned(),
|
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,
|
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 {
|
59 => Instruction::Variable {
|
||||||
result_type_id: operands[0],
|
result_type_id: operands[0],
|
||||||
result_id: operands[1],
|
result_id: operands[1],
|
||||||
storage_class: StorageClass::from_num(operands[2])?,
|
storage_class: StorageClass::from_num(operands[2])?,
|
||||||
initializer: operands.get(3).map(|&v| v),
|
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 {
|
71 => Instruction::Decorate {
|
||||||
target_id: operands[0],
|
target_id: operands[0],
|
||||||
decoration: Decoration::from_num(operands[1])?,
|
decoration: Decoration::from_num(operands[1])?,
|
||||||
@ -396,6 +650,138 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, Pars
|
|||||||
decoration_group: operands[0],
|
decoration_group: operands[0],
|
||||||
targets: operands.chunks(2).map(|x| (x[0], x[1])).collect(),
|
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 {
|
248 => Instruction::Label {
|
||||||
result_id: operands[0],
|
result_id: operands[0],
|
||||||
},
|
},
|
||||||
@ -404,6 +790,18 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, Pars
|
|||||||
},
|
},
|
||||||
252 => Instruction::Kill,
|
252 => Instruction::Kill,
|
||||||
253 => Instruction::Return,
|
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()),
|
_ => 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