Add runtime SPIR-V validation (#2460)

* Add runtime SPIR-V validation

* Remove copyright message

* fmt

* Up vk version in shader compilation (#2467)

* Up vk version in shader compilation

* Update test in codegen.rs

* cargo +nightly fmt

* Update lib.rs

* clippy + fmt fixes

* simplify changes

* post merge

* #2467 changelog

* Remove windows specific dnd disable (#2474)

* Fix unnecessarily strict validation for DRM format modifiers (#2469)

* #2469 changelog

* Add support for querying memory requirements directly from the device (#2470)

* #2470 changelog

* Make image_index and final_views accessible, and add new example. (#2473)

* Make image_index and final_views accessible, and new example.

The first 2 changes should make creating frame buffers easier.
The new example should make it easier to learn vulkano-util.

* Remove unnecessary imports, and run clippy.

* Run fmt.

* .acquire() no longer returns image_index

* rename final_views() to swapchain_image_views()

The name change makes it more consistent with swapchain_image_view().

Personally I don't understand why the field name is final_views, yet we externally in function names refer to it as swapchain image views and such like.

* Fractal example no longer creates framebuffer every frame.

* Game of life example no longer creates framebuffer every frame.

(Also removed a piece of code I had commented out, but had forgotten to remove from the fractal example.)

* Rename if_recreate_swapchain to on_recreate_swapchain and update acquire() documentation. to on_recreate_swapchain

* on_recreate_swapchain is now impl FnOnce instead of generics based FnMut

Thanks marc0246!

Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>

* Replace empty comment with an actual comment.

---------

Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>

* Fix `VulkanoWindowRenderer::swapchain_image_views` return type

I have only noticed this as I was writing the changelog.

@coolcatcoder for future reference, `&Vec<T>` is an anti-pattern. There's nothing more you can do with it than with `&[T]` (because the reference is immutable) and it means that we can't use a different underlying buffer without a breaking change.

* #2473 changelog

* Fix `VulkanoWindowRenderer::acquire` taking `&Vec<T>` as well

* Replace cgmath with glam in the examples (#2475)

* Replace cgmath with glam in the examples

* Implement type_for_format! for glam

* Remove comment where I'm freaking out because of OpenGL flashbacks

* Update Cargo.toml

Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>

* Update vulkano/autogen/formats.rs

Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>

* Fix glam type_for_format

* Format the code

---------

Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>

* #2475 changelog

* Fix alignment checks when allocating buffers (#2476)

* #2476 changelog

* Add `DepthState::reverse` helper method (#2483)

* #2483 changelog

* Add runtime SPIR-V validation

* Remove copyright message

* fmt

---------

Co-authored-by: maratik123 <marat.buharov@gmail.com>
Co-authored-by: Okko Hakola <okkohakola@gmail.com>
Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>
Co-authored-by: Katt <51190960+coolcatcoder@users.noreply.github.com>
Co-authored-by: stefnotch <stefnotch@users.noreply.github.com>
Co-authored-by: José Miguel Sánchez García <soy.jmi2k@gmail.com>
This commit is contained in:
Rua 2024-03-03 23:29:54 +01:00 committed by GitHub
parent 16973acdab
commit 4666a9c722
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 3677 additions and 196 deletions

View File

@ -4,6 +4,7 @@ use heck::ToSnakeCase;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use std::borrow::Cow;
// From the documentation of the OpSpecConstantOp instruction. // From the documentation of the OpSpecConstantOp instruction.
// The instructions requiring the Kernel capability are not listed, // The instructions requiring the Kernel capability are not listed,
@ -88,8 +89,19 @@ pub fn write(grammar: &SpirvGrammar) {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct InstructionMember { struct InstructionMember {
name: Ident, name: Ident,
is_atomic_operation: bool,
is_cooperative_matrix: bool,
is_cooperative_matrix_nv: bool,
is_group_operation: bool,
is_quad_group_operation: bool,
is_image_gather: bool,
is_image_fetch: bool,
is_image_sample: bool,
has_result_id: bool, has_result_id: bool,
has_result_type_id: bool, has_result_type_id: bool,
has_execution_scope_id: bool,
has_memory_scope_id: bool,
has_image_operands: Option<bool>,
opcode: u16, opcode: u16,
operands: Vec<OperandMember>, operands: Vec<OperandMember>,
} }
@ -187,6 +199,153 @@ fn instruction_output(members: &[InstructionMember], spec_constant: bool) -> Tok
} }
}, },
); );
let is_cooperative_matrix_items = members.iter().filter_map(
|InstructionMember {
name,
is_cooperative_matrix,
..
}| {
if *is_cooperative_matrix {
Some(quote! { Self::#name { .. } })
} else {
None
}
},
);
let is_cooperative_matrix_nv_items = members.iter().filter_map(
|InstructionMember {
name,
is_cooperative_matrix_nv,
..
}| {
if *is_cooperative_matrix_nv {
Some(quote! { Self::#name { .. } })
} else {
None
}
},
);
let is_group_operation_items = members.iter().filter_map(
|InstructionMember {
name,
is_group_operation,
..
}| {
if *is_group_operation {
Some(quote! { Self::#name { .. } })
} else {
None
}
},
);
let is_quad_group_operation_items = members.iter().filter_map(
|InstructionMember {
name,
is_quad_group_operation,
..
}| {
if *is_quad_group_operation {
Some(quote! { Self::#name { .. } })
} else {
None
}
},
);
let is_image_fetch_items = members.iter().filter_map(
|InstructionMember {
name,
is_image_fetch,
..
}| {
if *is_image_fetch {
Some(quote! { Self::#name { .. } })
} else {
None
}
},
);
let is_image_gather_items = members.iter().filter_map(
|InstructionMember {
name,
is_image_gather,
..
}| {
if *is_image_gather {
Some(quote! { Self::#name { .. } })
} else {
None
}
},
);
let is_image_sample_items = members.iter().filter_map(
|InstructionMember {
name,
is_image_sample,
..
}| {
if *is_image_sample {
Some(quote! { Self::#name { .. } })
} else {
None
}
},
);
let atomic_pointer_id_items = members.iter().filter_map(
|InstructionMember {
name,
is_atomic_operation,
..
}| {
if *is_atomic_operation {
Some(quote! { Self::#name { pointer, .. } })
} else {
None
}
},
);
let execution_scope_id_items = members.iter().filter_map(
|InstructionMember {
name,
has_execution_scope_id,
..
}| {
if *has_execution_scope_id {
Some(quote! { Self::#name { execution, .. } })
} else {
None
}
},
);
let memory_scope_id_items = members.iter().filter_map(
|InstructionMember {
name,
has_memory_scope_id,
..
}| {
if *has_memory_scope_id {
Some(quote! { Self::#name { memory, .. } })
} else {
None
}
},
);
let image_operands_items = members.iter().filter_map(
|InstructionMember {
name,
has_image_operands,
..
}| {
if let Some(has_image_operands) = *has_image_operands {
if has_image_operands {
Some(quote! { Self::#name { image_operands: Some(image_operands), .. } })
} else {
Some(quote! { Self::#name { image_operands, .. } })
}
} else {
None
}
},
);
quote! { quote! {
/// Returns the `Id` that is assigned by this instruction, if any. /// Returns the `Id` that is assigned by this instruction, if any.
@ -204,6 +363,94 @@ fn instruction_output(members: &[InstructionMember], spec_constant: bool) -> Tok
_ => None _ => None
} }
} }
/// Returns the `Id` of the pointer in an atomic operation, if any.
pub fn atomic_pointer_id(&self) -> Option<Id> {
match self {
#(#atomic_pointer_id_items)|* => Some(*pointer),
_ => None
}
}
/// Returns whether the instruction is a cooperative matrix instruction.
pub fn is_cooperative_matrix(&self) -> bool {
matches!(
self,
#(#is_cooperative_matrix_items)|*
)
}
/// Returns whether the instruction is an NV cooperative matrix instruction.
pub fn is_cooperative_matrix_nv(&self) -> bool {
matches!(
self,
#(#is_cooperative_matrix_nv_items)|*
)
}
/// Returns whether the instruction is a group operation instruction.
pub fn is_group_operation(&self) -> bool {
matches!(
self,
#(#is_group_operation_items)|*
)
}
/// Returns whether the instruction is a quad group operation instruction.
pub fn is_quad_group_operation(&self) -> bool {
matches!(
self,
#(#is_quad_group_operation_items)|*
)
}
/// Returns whether the instruction is an `ImageFetch*` instruction.
pub fn is_image_fetch(&self) -> bool {
matches!(
self,
#(#is_image_fetch_items)|*
)
}
/// Returns whether the instruction is an `Image*Gather` instruction.
pub fn is_image_gather(&self) -> bool {
matches!(
self,
#(#is_image_gather_items)|*
)
}
/// Returns whether the instruction is an `ImageSample*` instruction.
pub fn is_image_sample(&self) -> bool {
matches!(
self,
#(#is_image_sample_items)|*
)
}
/// Returns the `Id` of the execution scope ID operand, if any.
pub fn execution_scope_id(&self) -> Option<Id> {
match self {
#(#execution_scope_id_items)|* => Some(*execution),
_ => None
}
}
/// Returns the `Id` of the memory scope ID operand, if any.
pub fn memory_scope_id(&self) -> Option<Id> {
match self {
#(#memory_scope_id_items)|* => Some(*memory),
_ => None
}
}
/// Returns the image operands, if any.
pub fn image_operands(&self) -> Option<&ImageOperands> {
match self {
#(#image_operands_items)|* => Some(image_operands),
_ => None
}
}
} }
}; };
@ -242,9 +489,23 @@ fn instruction_members(grammar: &SpirvGrammar) -> Vec<InstructionMember> {
.instructions .instructions
.iter() .iter()
.map(|instruction| { .map(|instruction| {
let name = format_ident!("{}", instruction.opname.strip_prefix("Op").unwrap()); let name = instruction.opname.strip_prefix("Op").unwrap();
let is_atomic_operation = instruction.class == "Atomic";
let is_cooperative_matrix =
name.starts_with("CooperativeMatrix") && !name.ends_with("NV");
let is_cooperative_matrix_nv =
name.starts_with("CooperativeMatrix") && name.ends_with("NV");
let is_group_operation =
instruction.class == "Group" || instruction.class == "Non-Uniform";
let is_quad_group_operation = is_group_operation && instruction.opname.contains("Quad");
let is_image_fetch = name.starts_with("ImageFetch");
let is_image_gather = name.starts_with("Image") && name.ends_with("Gather");
let is_image_sample = name.starts_with("ImageSample");
let mut has_result_id = false; let mut has_result_id = false;
let mut has_result_type_id = false; let mut has_result_type_id = false;
let mut has_execution_scope_id = false;
let mut has_memory_scope_id = false;
let mut has_image_operands = None;
let mut operand_names = HashMap::default(); let mut operand_names = HashMap::default();
let mut operands = instruction let mut operands = instruction
@ -258,7 +519,23 @@ fn instruction_members(grammar: &SpirvGrammar) -> Vec<InstructionMember> {
has_result_type_id = true; has_result_type_id = true;
format_ident!("result_type_id") format_ident!("result_type_id")
} else { } else {
to_member_name(&operand.kind, operand.name.as_deref()) let member_name = to_member_name(&operand.kind, operand.name.as_deref());
if operand.kind == "IdScope" {
if member_name == "execution" {
has_execution_scope_id = true;
} else if member_name == "memory" {
has_memory_scope_id = true;
}
} else if operand.kind == "ImageOperands" {
if operand.quantifier == Some('?') {
has_image_operands = Some(true);
} else {
has_image_operands = Some(false);
}
}
format_ident!("{}", member_name)
}; };
*operand_names.entry(name.clone()).or_insert(0) += 1; *operand_names.entry(name.clone()).or_insert(0) += 1;
@ -305,9 +582,20 @@ fn instruction_members(grammar: &SpirvGrammar) -> Vec<InstructionMember> {
} }
InstructionMember { InstructionMember {
name, name: format_ident!("{}", name),
is_atomic_operation,
is_cooperative_matrix,
is_cooperative_matrix_nv,
is_group_operation,
is_quad_group_operation,
is_image_fetch,
is_image_gather,
is_image_sample,
has_result_id, has_result_id,
has_result_type_id, has_result_type_id,
has_execution_scope_id,
has_memory_scope_id,
has_image_operands,
opcode: instruction.opcode, opcode: instruction.opcode,
operands, operands,
} }
@ -465,7 +753,10 @@ fn bit_enum_members(grammar: &SpirvGrammar) -> Vec<(Ident, Vec<KindEnumMember>)>
.parameters .parameters
.iter() .iter()
.map(|param| { .map(|param| {
let name = to_member_name(&param.kind, param.name.as_deref()); let name = format_ident!(
"{}",
to_member_name(&param.kind, param.name.as_deref())
);
let (ty, parse) = parameter_kinds[param.kind.as_str()].clone(); let (ty, parse) = parameter_kinds[param.kind.as_str()].clone();
OperandMember { name, ty, parse } OperandMember { name, ty, parse }
@ -614,7 +905,10 @@ fn value_enum_members(grammar: &SpirvGrammar) -> Vec<(Ident, Vec<KindEnumMember>
.parameters .parameters
.iter() .iter()
.map(|param| { .map(|param| {
let name = to_member_name(&param.kind, param.name.as_deref()); let name = format_ident!(
"{}",
to_member_name(&param.kind, param.name.as_deref())
);
let (ty, parse) = parameter_kinds[param.kind.as_str()].clone(); let (ty, parse) = parameter_kinds[param.kind.as_str()].clone();
OperandMember { name, ty, parse } OperandMember { name, ty, parse }
@ -634,24 +928,24 @@ fn value_enum_members(grammar: &SpirvGrammar) -> Vec<(Ident, Vec<KindEnumMember>
.collect() .collect()
} }
fn to_member_name(kind: &str, name: Option<&str>) -> Ident { fn to_member_name(kind: &str, name: Option<&str>) -> Cow<'static, str> {
if let Some(name) = name { if let Some(name) = name {
let name = name.to_snake_case(); let name = name.to_snake_case();
// Fix some weird names // Fix some weird names
match name.as_str() { match name.as_str() {
"argument_0_argument_1" => format_ident!("arguments"), "argument_0_argument_1" => "arguments".into(),
"member_0_type_member_1_type" => format_ident!("member_types"), "member_0_type_member_1_type" => "member_types".into(),
"operand_1_operand_2" => format_ident!("operands"), "operand_1_operand_2" => "operands".into(),
"parameter_0_type_parameter_1_type" => format_ident!("parameter_types"), "parameter_0_type_parameter_1_type" => "parameter_types".into(),
"the_name_of_the_opaque_type" => format_ident!("name"), "the_name_of_the_opaque_type" => "name".into(),
"d_ref" => format_ident!("dref"), "d_ref" => "dref".into(),
"type" => format_ident!("ty"), // type is a keyword "type" => "ty".into(), // type is a keyword
"use" => format_ident!("usage"), // use is a keyword "use" => "usage".into(), // use is a keyword
_ => format_ident!("{}", name.replace("operand_", "operand")), _ => name.replace("operand_", "operand").into(),
} }
} else { } else {
format_ident!("{}", kind.to_snake_case()) kind.to_snake_case().into()
} }
} }

View File

@ -209,11 +209,11 @@ fn get_variables_by_key<'a>(
variable_id, variable_id,
filter_storage_class, filter_storage_class,
|key, data| { |key, data| {
if let InputOutputKey::Location { if let InputOutputKey::User(InputOutputUserKey {
location, location,
component, component,
.. ..
} = key }) = key
{ {
let InputOutputData { let InputOutputData {
variable_id, variable_id,
@ -646,6 +646,15 @@ pub(crate) enum ShaderInterfaceLocationWidth {
Bits64, Bits64,
} }
impl ShaderInterfaceLocationWidth {
pub(crate) fn component_count(self) -> u32 {
match self {
ShaderInterfaceLocationWidth::Bits32 => 1,
ShaderInterfaceLocationWidth::Bits64 => 2,
}
}
}
impl From<u32> for ShaderInterfaceLocationWidth { impl From<u32> for ShaderInterfaceLocationWidth {
#[inline] #[inline]
fn from(value: u32) -> Self { fn from(value: u32) -> Self {
@ -662,138 +671,6 @@ pub(crate) fn shader_interface_location_info(
entry_point_id: Id, entry_point_id: Id,
filter_storage_class: StorageClass, filter_storage_class: StorageClass,
) -> HashMap<u32, ShaderInterfaceLocationInfo> { ) -> HashMap<u32, ShaderInterfaceLocationInfo> {
fn add_type(
locations: &mut HashMap<u32, ShaderInterfaceLocationInfo>,
spirv: &Spirv,
mut location: u32,
mut component: u32,
index: u32,
type_id: Id,
) -> (u32, u32) {
debug_assert!(component < 4);
let mut add_scalar = |numeric_type: NumericType, width: u32| -> (u32, u32) {
let width = ShaderInterfaceLocationWidth::from(width);
let components_to_add = match width {
ShaderInterfaceLocationWidth::Bits32 => {
ColorComponents::from_index(component as usize)
}
ShaderInterfaceLocationWidth::Bits64 => {
debug_assert!(component & 1 == 0);
ColorComponents::from_index(component as usize)
| ColorComponents::from_index(component as usize + 1)
}
};
let location_info = match locations.entry(location) {
Entry::Occupied(entry) => {
let location_info = entry.into_mut();
debug_assert_eq!(location_info.numeric_type, numeric_type);
debug_assert_eq!(location_info.width, width);
location_info
}
Entry::Vacant(entry) => entry.insert(ShaderInterfaceLocationInfo {
numeric_type,
width,
components: [ColorComponents::empty(); 2],
}),
};
let components = &mut location_info.components[index as usize];
debug_assert!(!components.intersects(components_to_add));
*components |= components_to_add;
(components_to_add.count(), 1)
};
match *spirv.id(type_id).instruction() {
Instruction::TypeInt {
width, signedness, ..
} => {
let numeric_type = if signedness == 1 {
NumericType::Int
} else {
NumericType::Uint
};
add_scalar(numeric_type, width)
}
Instruction::TypeFloat { width, .. } => add_scalar(NumericType::Float, width),
Instruction::TypeVector {
component_type,
component_count,
..
} => {
let mut total_locations_added = 1;
for _ in 0..component_count {
// Overflow into next location
if component == 4 {
component = 0;
location += 1;
total_locations_added += 1;
} else {
debug_assert!(component < 4);
}
let (_, components_added) =
add_type(locations, spirv, location, component, index, component_type);
component += components_added;
}
(total_locations_added, 0)
}
Instruction::TypeMatrix {
column_type,
column_count,
..
} => {
let mut total_locations_added = 0;
for _ in 0..column_count {
let (locations_added, _) =
add_type(locations, spirv, location, component, index, column_type);
location += locations_added;
total_locations_added += locations_added;
}
(total_locations_added, 0)
}
Instruction::TypeArray {
element_type,
length,
..
} => {
let length = get_constant(spirv, length).unwrap();
let mut total_locations_added = 0;
for _ in 0..length {
let (locations_added, _) =
add_type(locations, spirv, location, component, index, element_type);
location += locations_added;
total_locations_added += locations_added;
}
(total_locations_added, 0)
}
Instruction::TypeStruct {
ref member_types, ..
} => {
let mut total_locations_added = 0;
for &member_type in member_types {
let (locations_added, _) =
add_type(locations, spirv, location, component, index, member_type);
location += locations_added;
total_locations_added += locations_added;
}
(total_locations_added, 0)
}
_ => unimplemented!(),
}
}
let (execution_model, interface) = match spirv.function(entry_point_id).entry_point() { let (execution_model, interface) = match spirv.function(entry_point_id).entry_point() {
Some(&Instruction::EntryPoint { Some(&Instruction::EntryPoint {
execution_model, execution_model,
@ -803,7 +680,42 @@ pub(crate) fn shader_interface_location_info(
_ => unreachable!(), _ => unreachable!(),
}; };
let mut locations = HashMap::default(); let mut locations: HashMap<u32, ShaderInterfaceLocationInfo> = HashMap::default();
let mut scalar_func = |key: InputOutputUserKey,
width: ShaderInterfaceLocationWidth,
numeric_type: NumericType| {
let InputOutputUserKey {
location,
component,
index,
} = key;
let location_info = match locations.entry(location) {
Entry::Occupied(entry) => {
let location_info = entry.into_mut();
debug_assert_eq!(location_info.numeric_type, numeric_type);
debug_assert_eq!(location_info.width, width);
location_info
}
Entry::Vacant(entry) => entry.insert(ShaderInterfaceLocationInfo {
numeric_type,
width,
components: [ColorComponents::empty(); 2],
}),
};
let components = &mut location_info.components[index as usize];
let components_to_add = match width {
ShaderInterfaceLocationWidth::Bits32 => ColorComponents::from_index(component as usize),
ShaderInterfaceLocationWidth::Bits64 => {
debug_assert!(component & 1 == 0);
ColorComponents::from_index(component as usize)
| ColorComponents::from_index(component as usize + 1)
}
};
debug_assert!(!components.intersects(components_to_add));
*components |= components_to_add;
};
for &variable_id in interface { for &variable_id in interface {
input_output_map( input_output_map(
@ -812,14 +724,9 @@ pub(crate) fn shader_interface_location_info(
variable_id, variable_id,
filter_storage_class, filter_storage_class,
|key, data| { |key, data| {
if let InputOutputKey::Location { if let InputOutputKey::User(key) = key {
location,
component,
index,
} = key
{
let InputOutputData { type_id, .. } = data; let InputOutputData { type_id, .. } = data;
add_type(&mut locations, spirv, location, component, index, type_id); shader_interface_analyze_type(spirv, type_id, key, &mut scalar_func);
} }
}, },
); );
@ -828,16 +735,121 @@ pub(crate) fn shader_interface_location_info(
locations locations
} }
/// Recursively analyzes the type `type_id` with the given `key`. Calls `scalar_func` on every
/// scalar type that is encountered, and returns the number of locations and components to advance.
pub(crate) fn shader_interface_analyze_type(
spirv: &Spirv,
type_id: Id,
mut key: InputOutputUserKey,
scalar_func: &mut impl FnMut(InputOutputUserKey, ShaderInterfaceLocationWidth, NumericType),
) -> (u32, u32) {
debug_assert!(key.component < 4);
match *spirv.id(type_id).instruction() {
Instruction::TypeInt {
width, signedness, ..
} => {
let numeric_type = if signedness == 1 {
NumericType::Int
} else {
NumericType::Uint
};
let width = ShaderInterfaceLocationWidth::from(width);
scalar_func(key, width, numeric_type);
(1, width.component_count())
}
Instruction::TypeFloat { width, .. } => {
let width = ShaderInterfaceLocationWidth::from(width);
scalar_func(key, width, NumericType::Float);
(1, width.component_count())
}
Instruction::TypeVector {
component_type,
component_count,
..
} => {
let mut total_locations_added = 1;
for _ in 0..component_count {
// Overflow into next location
if key.component == 4 {
key.component = 0;
key.location += 1;
total_locations_added += 1;
} else {
debug_assert!(key.component < 4);
}
let (_, components_added) =
shader_interface_analyze_type(spirv, component_type, key, scalar_func);
key.component += components_added;
}
(total_locations_added, 0)
}
Instruction::TypeMatrix {
column_type,
column_count,
..
} => {
let mut total_locations_added = 0;
for _ in 0..column_count {
let (locations_added, _) =
shader_interface_analyze_type(spirv, column_type, key, scalar_func);
key.location += locations_added;
total_locations_added += locations_added;
}
(total_locations_added, 0)
}
Instruction::TypeArray {
element_type,
length,
..
} => {
let length = get_constant(spirv, length).unwrap();
let mut total_locations_added = 0;
for _ in 0..length {
let (locations_added, _) =
shader_interface_analyze_type(spirv, element_type, key, scalar_func);
key.location += locations_added;
total_locations_added += locations_added;
}
(total_locations_added, 0)
}
Instruction::TypeStruct {
ref member_types, ..
} => {
let mut total_locations_added = 0;
for &member_type in member_types {
let (locations_added, _) =
shader_interface_analyze_type(spirv, member_type, key, scalar_func);
key.location += locations_added;
total_locations_added += locations_added;
}
(total_locations_added, 0)
}
_ => unimplemented!(),
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum InputOutputKey { pub(crate) enum InputOutputKey {
Location { User(InputOutputUserKey),
location: u32, BuiltIn(BuiltIn),
component: u32, }
index: u32,
}, #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
BuiltIn { pub(crate) struct InputOutputUserKey {
built_in: BuiltIn, pub(crate) location: u32,
}, pub(crate) component: u32,
pub(crate) index: u32,
} }
pub(crate) struct InputOutputData { pub(crate) struct InputOutputData {
@ -895,11 +907,11 @@ pub(crate) fn input_output_map(
if let Some(location) = location { if let Some(location) = location {
func( func(
InputOutputKey::Location { InputOutputKey::User(InputOutputUserKey {
location, location,
component, component,
index, index,
}, }),
InputOutputData { InputOutputData {
variable_id, variable_id,
pointer_type_id, pointer_type_id,
@ -909,7 +921,7 @@ pub(crate) fn input_output_map(
); );
} else if let Some(built_in) = built_in { } else if let Some(built_in) = built_in {
func( func(
InputOutputKey::BuiltIn { built_in }, InputOutputKey::BuiltIn(built_in),
InputOutputData { InputOutputData {
variable_id, variable_id,
pointer_type_id, pointer_type_id,
@ -949,11 +961,11 @@ pub(crate) fn input_output_map(
if let Some(location) = location { if let Some(location) = location {
func( func(
InputOutputKey::Location { InputOutputKey::User(InputOutputUserKey {
location, location,
component, component,
index, index,
}, }),
InputOutputData { InputOutputData {
variable_id, variable_id,
pointer_type_id, pointer_type_id,
@ -966,7 +978,7 @@ pub(crate) fn input_output_map(
); );
} else if let Some(built_in) = built_in { } else if let Some(built_in) = built_in {
func( func(
InputOutputKey::BuiltIn { built_in }, InputOutputKey::BuiltIn(built_in),
InputOutputData { InputOutputData {
variable_id, variable_id,
pointer_type_id, pointer_type_id,

View File

@ -9,6 +9,7 @@ use crate::{
}; };
pub(crate) mod inout_interface; pub(crate) mod inout_interface;
pub(crate) mod validate_runtime;
/// Specifies a single shader stage when creating a pipeline. /// Specifies a single shader stage when creating a pipeline.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -62,6 +63,9 @@ impl PipelineShaderStageCreateInfo {
} = self; } = self;
let spirv = entry_point.module().spirv(); let spirv = entry_point.module().spirv();
validate_runtime::validate_runtime(device, spirv, entry_point.id())
.map_err(|err| err.add_context("entry_point"))?;
let properties = device.physical_device().properties(); let properties = device.physical_device().properties();
flags.validate_device(device).map_err(|err| { flags.validate_device(device).map_err(|err| {

File diff suppressed because it is too large Load Diff

View File

@ -138,13 +138,23 @@
//! then if the shader accesses a descriptor in that binding, the descriptor must be initialized //! then if the shader accesses a descriptor in that binding, the descriptor must be initialized
//! and contain a valid resource. //! and contain a valid resource.
//! //!
//! ## Buffers //! ## Buffers and memory accesses
//! //!
//! - If the [`robust_buffer_access`](Features::robust_buffer_access) feature is not enabled on the //! - If the [`robust_buffer_access`](Features::robust_buffer_access) feature is not enabled on the
//! device, then the shader must not access any values outside the range of the buffer, as //! device, then the shader must not access any values outside the range of the buffer, as
//! specified when writing the descriptor set. <sup>[\[06935\]] [\[06936\]]</sup> //! specified when writing the descriptor set. <sup>[\[06935\]] [\[06936\]]</sup>
//! - If any `PhysicalStorageBuffer` pointers to device memory are dereferenced in the shader, then //! - If any `PhysicalStorageBuffer` pointers to device memory are dereferenced in the shader,
//! they must point to valid buffer memory of the correct type. //! then:
//! - The pointer must point to valid memory of the correct type.
//! - The pointer must be aligned to a multiple of the largest scalar type within the type that
//! it points to. <sup>[\[06314\]]</sup>
//! - If the instruction has `Aligned` as one of its memory operands, the pointer must be aligned
//! to the specified alignment. <sup>[\[06315\]]</sup>
//! - For `OpCooperativeMatrixLoadKHR`, `OpCooperativeMatrixStoreKHR`, `OpCooperativeMatrixLoadNV`
//! and `OpCooperativeMatrixStoreNV` instructions, the `Pointer` and `Stride` operands must both
//! be aligned to the minimum of either 16 bytes or the number of bytes per row/column of the
//! matrix (depending on the `ColumnMajor` and `RowMajor` decorations). <sup>[\[06324\]]
//! [\[08986\]]</sup>
//! //!
//! ## Image views and buffer views //! ## Image views and buffer views
//! //!
@ -160,6 +170,8 @@
//! only if the format of the bound image view or buffer view also has a 64-bit component. //! only if the format of the bound image view or buffer view also has a 64-bit component.
//! Otherwise, it must have a `Width` of 32. <sup>[\[04470\]] [\[04471\]] [\[04472\]] //! Otherwise, it must have a `Width` of 32. <sup>[\[04470\]] [\[04471\]] [\[04472\]]
//! [\[04473\]]</sup> //! [\[04473\]]</sup>
//! - The [`samples`](Image::samples) of the underlying image of the bound image view must match
//! the `MS` operand of the `OpImageType`. <sup>[\[08725\]] [\[08726\]]</sup>
//! - For a storage image/texel buffer declared with `OpTypeImage` with an `Unknown` format: //! - For a storage image/texel buffer declared with `OpTypeImage` with an `Unknown` format:
//! - If it is written to in the shader, the format of the bound image view or buffer view must //! - If it is written to in the shader, the format of the bound image view or buffer view must
//! have the [`FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT`] format feature. <sup>[\[07027\]] //! have the [`FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT`] format feature. <sup>[\[07027\]]
@ -209,54 +221,197 @@
//! - The sampler must not be used with the `ConstOffset` or `Offset` image operands. //! - The sampler must not be used with the `ConstOffset` or `Offset` image operands.
//! <sup>[\[06551\]]</sup> //! <sup>[\[06551\]]</sup>
//! //!
//! ## Acceleration structures //! ## Mesh shading
//! //!
//! - If the shader declares the `OutputPoints` execution mode with a value greater than 0, and the
//! [`maintenance5`](Features::maintenance5) feature is not enabled on the device, then the
//! shader must write to a variable decorated with `PointSize` for each output point.
//! <sup>[\[09218\]]</sup>
//!
//! For `OpSetMeshOutputsEXT` instructions:
//!
//! - The `Vertex Count` operand must be less than or equal to the value declared with the shader's
//! `OutputVertices` execution mode. <sup>[\[07332\]]</sup>
//! - The `Primitive Count` operand must be less than or equal to the value declared with the
//! shader's `OutputPrimitivesEXT` execution mode. <sup>[\[07333\]]</sup>
//!
//! ## Acceleration structures, ray queries and ray tracing
//!
//! - Acceleration structures that are used as operands to an instruction must have been built as a
//! top-level acceleration structure. <sup>[\[06352\]] [\[06359\]] [\[06365\]] [\[07709\]]</sup>
//! - In any top-level acceleration structure, the pointers that refer to the contained //! - In any top-level acceleration structure, the pointers that refer to the contained
//! bottom-level acceleration structure instances must point to valid acceleration structures. //! bottom-level acceleration structure instances must point to valid bottom-level acceleration
//! structures.
//!
//! For `OpRayQueryInitializeKHR` and `OpTraceRayKHR` instructions:
//!
//! - The `Rayflags` operand must not contain more than one of:
//! - `SkipTrianglesKHR`, `CullBackFacingTrianglesKHR` and `CullFrontFacingTrianglesKHR`
//! <sup>[\[06889\]] [\[06892\]]</sup>
//! - `SkipTrianglesKHR` and `SkipAABBsKHR` <sup>[\[06890\]] [\[06552\]] [\[07712\]]</sup>
//! - `OpaqueKHR`, `NoOpaqueKHR`, `CullOpaqueKHR`, and `CullNoOpaqueKHR` <sup>[\[06891\]]
//! [\[06893\]]</sup>
//! - The `RayOrigin` and `RayDirection` operands must not contain infinite or NaN values. <sup>
//! [\[06348\]] [\[06351\]] [\[06355\]] [\[06358\]] </sup>
//! - The `RayTmin` and `RayTmax` operands must not contain negative or NaN values, and `RayTmin`
//! must be less than or equal to `RayTmax`. <sup> [\[06349\]] [\[06350\]] [\[06351\]]
//! [\[06356\]] [\[06357\]] [\[06358\]] </sup>
//!
//! For `OpRayQueryGenerateIntersectionKHR` instructions:
//!
//! - The `Hit T` operand must be greater than or equal to the value that would be returned by
//! `OpRayQueryGetRayTMinKHR`. <sup>[\[06353\]]</sup>
//! - The `Hit T` operand must be less than or equal to the value that would be returned by
//! `OpRayQueryGetIntersectionTKHR` for the current committed intersection.
//! <sup>[\[06353\]]</sup>
//!
//! For `OpReportIntersectionKHR` instructions:
//!
//! - The `Hit Kind` operand must be between 0 and 127 inclusive. <sup>[\[06998\]]</sup>
//!
//! ## Dynamically uniform values and control flow
//!
//! In a shader, a value (expression, variable) is *[dynamically uniform]* if its value is the same
//! for all shader invocations within an *invocation group*. What counts as an invocation group
//! depends on the type of shader being executed:
//!
//! - For compute, task and mesh shaders, an invocation group is the same as the (local) workgroup.
//! A single `dispatch` command value spawns one distinct invocation group for every element in
//! the product of the given `group_counts` argument.
//! - For all other graphics shaders, an invocation group is all shaders invoked by a single draw
//! command. For indirect draws, each element of the indirect buffer creates one draw call.
//! - For ray tracing shaders, an invocation group is an implementation-dependent subset of the
//! shaders invoked by a single ray tracing command.
//!
//! Vulkan and SPIR-V assume that certain values within a shader are dynamically uniform, and will
//! optimize the generated shader code accordingly. If such a value is not actually dynamically
//! uniform, this results in undefined behavior. This concerns the following values:
//!
//! - The index into an arrayed descriptor binding. If the index is not dynamically uniform, you
//! must explicitly mark it with the `NonUniform` decoration in SPIR-V, or the `nonuniformEXT`
//! function in GLSL. <sup>[\[06274\]]</sup>
//! - The `Index` argument of the `OpGroupNonUniformQuadBroadcast` instruction.
//! <sup>[\[06276\]]</sup>
//! - The `Id` argument of the `OpGroupNonUniformBroadcast` instruction. <sup>[\[06277\]]</sup>
//! - The arguments of the `OpEmitMeshTasksEXT` and `OpSetMeshOutputsEXT` instructions.
//! <sup>[\[07117\]] [\[07118\]]</sup>
//! - The `Texture Sampled Image` and `Weight Image` arguments of the `OpImageWeightedSampleQCOM`
//! instruction. <sup>[\[06979\]]</sup>
//! - The `Texture Sampled Image`, `Reference Sampled Image` and `Block Size` arguments of the
//! `OpImageBlockMatchSADQCOM` and `OpImageBlockMatchSSDQCOM` instructions.
//! <sup>[\[06982\]]</sup>
//! - The `Sampled Texture Image` and `Box Size` arguments of the `OpImageBoxFilterQCOM`
//! instruction. <sup>[\[06990\]]</sup>
//! - The `Target Sampled Image`, `Reference Sampled Image` and `Block Size` arguments of any
//! `OpImageBlockMatchWindow*QCOM` or `OpImageBlockMatchGather*QCOM` instructions.
//! <sup>[\[09219\]]</sup>
//!
//! Some operations have specific requirements for control flow within the shader:
//!
//! - The `OpEmitMeshTasksEXT` and `OpSetMeshOutputsEXT` instructions must be executed uniformly
//! within the invocation group. That means that, either all shader invocations within the
//! invocation group must execute the instruction, or none of them must execute it.
//! <sup>[\[07117\]] [\[07118\]]</sup>
//! - If the `PointSize` built-in is written to, then all execution paths must write to it.
//! <sup>[\[09190\]]</sup>
//! //!
//! [alignment rules]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html#interfaces-resources-layout //! [alignment rules]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html#interfaces-resources-layout
//! [`GL_EXT_scalar_block_layout`]: https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_scalar_block_layout.txt //! [`GL_EXT_scalar_block_layout`]: https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_scalar_block_layout.txt
//! [`scalar_block_layout`]: crate::device::Features::scalar_block_layout //! [`scalar_block_layout`]: Features::scalar_block_layout
//! [`uniform_buffer_standard_layout`]: crate::device::Features::uniform_buffer_standard_layout //! [`uniform_buffer_standard_layout`]: Features::uniform_buffer_standard_layout
//! [\[06935\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-uniformBuffers-06935 //! [dynamically uniform]: https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_uniformity
//! [\[06936\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-storageBuffers-06936 //! [\[02691\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-02691
//! [\[07752\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-viewType-07752 //! [\[02692\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-02692
//! [\[07753\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-format-07753 //! [\[02694\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-filterCubic-02694
//! [\[02695\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-filterCubicMinmax-02695
//! [\[04469\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpImageWrite-04469 //! [\[04469\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpImageWrite-04469
//! [\[08795\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpImageWrite-08795
//! [\[08796\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpImageWrite-08796
//! [\[04470\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04470 //! [\[04470\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04470
//! [\[04471\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04471 //! [\[04471\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04471
//! [\[04472\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04472 //! [\[04472\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04472
//! [\[04473\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04473 //! [\[04473\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04473
//! [\[04553\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-magFilter-04553
//! [\[04770\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-mipmapMode-04770
//! [\[06274\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-NonUniform-06274
//! [\[06276\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-subgroupBroadcastDynamicId-06276
//! [\[06277\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-subgroupBroadcastDynamicId-06277
//! [\[06314\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-PhysicalStorageBuffer64-06314
//! [\[06315\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-PhysicalStorageBuffer64-06315
//! [\[06324\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpCooperativeMatrixLoadNV-06324
//! [\[06348\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06348
//! [\[06349\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06349
//! [\[06350\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06350
//! [\[06351\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06351
//! [\[06352\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06352
//! [\[06353\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpRayQueryGenerateIntersectionKHR-06353
//! [\[06355\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayKHR-06355
//! [\[06356\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayKHR-06356
//! [\[06357\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayKHR-06357
//! [\[06358\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayKHR-06358
//! [\[06359\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayKHR-06359
//! [\[06361\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayMotionNV-06361
//! [\[06362\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayMotionNV-06362
//! [\[06363\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayMotionNV-06363
//! [\[06364\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayMotionNV-06364
//! [\[06365\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayMotionNV-06365
//! [\[06366\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayMotionNV-06366
//! [\[06479\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-06479
//! [\[06550\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-06550
//! [\[06551\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-ConstOffset-06551
//! [\[06552\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayKHR-06552
//! [\[06889\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06889
//! [\[06890\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06890
//! [\[06891\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06891
//! [\[06892\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayKHR-06892
//! [\[06893\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpTraceRayKHR-06893
//! [\[06935\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-uniformBuffers-06935
//! [\[06936\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-storageBuffers-06936
//! [\[06979\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpImageWeightedSampleQCOM-06979
//! [\[06982\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpImageBlockMatchSADQCOM-06982
//! [\[06990\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpImageBoxFilterQCOM-06990
//! [\[06998\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpReportIntersectionKHR-06998
//! [\[07027\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07027 //! [\[07027\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07027
//! [\[07029\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07029 //! [\[07029\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07029
//! [\[07028\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07028 //! [\[07028\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07028
//! [\[07030\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07030 //! [\[07030\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07030
//! [\[02691\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-02691 //! [\[07117\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-TaskEXT-07117
//! [\[07118\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-MeshEXT-07118
//! [\[07332\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-MeshEXT-07332
//! [\[07333\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-MeshEXT-07333
//! [\[07705\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpHitObjectTraceRayNV-07705
//! [\[07706\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpHitObjectTraceRayNV-07706
//! [\[07707\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpHitObjectTraceRayNV-07707
//! [\[07708\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpHitObjectTraceRayNV-07708
//! [\[07709\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpHitObjectTraceRayMotionNV-07709
//! [\[07710\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpHitObjectTraceRayNV-07710
//! [\[07712\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpHitObjectTraceRayNV-07712
//! [\[07713\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpHitObjectTraceRayNV-07713
//! [\[07714\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpHitObjectTraceRayNV-07714
//! [\[07752\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-viewType-07752
//! [\[07753\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-format-07753
//! [\[07888\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-07888 //! [\[07888\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-07888
//! [\[04553\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-magFilter-04553
//! [\[04770\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-mipmapMode-04770
//! [\[02692\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-02692
//! [\[02694\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-filterCubic-02694
//! [\[02695\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-filterCubicMinmax-02695
//! [\[06479\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-06479
//! [\[08609\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-08609 //! [\[08609\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-08609
//! [\[08610\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-08610 //! [\[08610\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-08610
//! [\[08611\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-08611 //! [\[08611\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-08611
//! [\[06550\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-06550 //! [\[08725\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-samples-08725
//! [\[06551\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-ConstOffset-06551 //! [\[08726\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-samples-08726
//! [\[08795\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpImageWrite-08795
//! [\[08796\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpImageWrite-08796
//! [\[08986\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpCooperativeMatrixLoadKHR-08986
//! [\[09190\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-maintenance5-09190
//! [\[09218\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-MeshEXT-09218
//! [\[09219\]]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-RuntimeSpirv-OpImageBlockMatchWindow-09219
use self::spirv::{Id, Instruction}; use self::spirv::{Id, Instruction};
#[cfg(doc)] #[cfg(doc)]
use crate::{ use crate::{
acceleration_structure::BuildAccelerationStructureFlags,
descriptor_set::layout::DescriptorBindingFlags, descriptor_set::layout::DescriptorBindingFlags,
device::{physical::PhysicalDevice, Features}, device::{physical::PhysicalDevice, Features, Properties},
format::FormatFeatures, format::FormatFeatures,
image::{ image::{
sampler::{Filter, Sampler, SamplerCreateInfo, SamplerMipmapMode, SamplerReductionMode}, sampler::{Filter, Sampler, SamplerCreateInfo, SamplerMipmapMode, SamplerReductionMode},
view::ImageView, view::ImageView,
ImageFormatProperties, Image, ImageFormatProperties,
}, },
}; };
use crate::{ use crate::{

View File

@ -15,6 +15,7 @@ use crate::{
}; };
use ahash::{HashMap, HashSet}; use ahash::{HashMap, HashSet};
use half::f16; use half::f16;
use smallvec::{smallvec, SmallVec};
use std::borrow::Cow; use std::borrow::Cow;
/// Returns an iterator over all entry points in `spirv`, with information about the entry point. /// Returns an iterator over all entry points in `spirv`, with information about the entry point.
@ -1450,6 +1451,96 @@ pub(crate) fn get_constant(spirv: &Spirv, id: Id) -> Option<u64> {
} }
} }
pub(crate) fn get_constant_composite(spirv: &Spirv, id: Id) -> Option<SmallVec<[u64; 4]>> {
match spirv.id(id).instruction() {
Instruction::ConstantComposite { constituents, .. } => Some(
constituents
.iter()
.map(|&id| match spirv.id(id).instruction() {
Instruction::Constant { value, .. } => match value.len() {
1 => value[0] as u64,
2 => value[0] as u64 | (value[1] as u64) << 32,
_ => panic!("constant {} is larger than 64 bits", id),
},
_ => unreachable!(),
})
.collect(),
),
_ => None,
}
}
pub(crate) fn get_constant_float_composite(spirv: &Spirv, id: Id) -> Option<SmallVec<[f64; 4]>> {
match spirv.id(id).instruction() {
Instruction::ConstantComposite { constituents, .. } => Some(
constituents
.iter()
.map(|&id| match spirv.id(id).instruction() {
Instruction::Constant { value, .. } => match value.len() {
1 => f32::from_bits(value[0]) as f64,
2 => f64::from_bits(value[0] as u64 | (value[1] as u64) << 32),
_ => panic!("constant {} is larger than 64 bits", id),
},
_ => unreachable!(),
})
.collect(),
),
_ => None,
}
}
pub(crate) fn get_constant_maybe_composite(spirv: &Spirv, id: Id) -> Option<SmallVec<[u64; 4]>> {
match spirv.id(id).instruction() {
Instruction::Constant { value, .. } => match value.len() {
1 => Some(smallvec![value[0] as u64]),
2 => Some(smallvec![value[0] as u64 | (value[1] as u64) << 32]),
_ => panic!("constant {} is larger than 64 bits", id),
},
Instruction::ConstantComposite { constituents, .. } => Some(
constituents
.iter()
.map(|&id| match spirv.id(id).instruction() {
Instruction::Constant { value, .. } => match value.len() {
1 => value[0] as u64,
2 => value[0] as u64 | (value[1] as u64) << 32,
_ => panic!("constant {} is larger than 64 bits", id),
},
_ => unreachable!(),
})
.collect(),
),
_ => None,
}
}
pub(crate) fn get_constant_composite_composite(
spirv: &Spirv,
id: Id,
) -> Option<SmallVec<[SmallVec<[u64; 4]>; 4]>> {
match spirv.id(id).instruction() {
Instruction::ConstantComposite { constituents, .. } => Some(
constituents
.iter()
.map(|&id| match spirv.id(id).instruction() {
Instruction::ConstantComposite { constituents, .. } => constituents
.iter()
.map(|&id| match spirv.id(id).instruction() {
Instruction::Constant { value, .. } => match value.len() {
1 => value[0] as u64,
2 => value[0] as u64 | (value[1] as u64) << 32,
_ => panic!("constant {} is larger than 64 bits", id),
},
_ => unreachable!(),
})
.collect(),
_ => unreachable!(),
})
.collect(),
),
_ => None,
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{HashMap, PushConstantRange, ShaderStages, Version}; use super::{HashMap, PushConstantRange, ShaderStages, Version};