mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-25 08:14:20 +00:00
Properly validate shader interfaces between stages (#2418)
* Properly validate shader interfaces between stages * Update vulkano/src/pipeline/shader.rs Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com> --------- Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>
This commit is contained in:
parent
5c98e5290a
commit
c70876b3b9
@ -60,8 +60,8 @@ use self::{
|
|||||||
viewport::ViewportState,
|
viewport::ViewportState,
|
||||||
};
|
};
|
||||||
use super::{
|
use super::{
|
||||||
cache::PipelineCache, DynamicState, Pipeline, PipelineBindPoint, PipelineCreateFlags,
|
cache::PipelineCache, shader::validate_interfaces_compatible, DynamicState, Pipeline,
|
||||||
PipelineLayout, PipelineShaderStageCreateInfo,
|
PipelineBindPoint, PipelineCreateFlags, PipelineLayout, PipelineShaderStageCreateInfo,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
device::{Device, DeviceOwned, DeviceOwnedDebugWrapper},
|
device::{Device, DeviceOwned, DeviceOwnedDebugWrapper},
|
||||||
@ -1996,29 +1996,50 @@ impl GraphicsPipelineCreateInfo {
|
|||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// TODO: this check is too strict; the output only has to be a superset, any variables
|
|
||||||
// not used in the input of the next shader are just ignored.
|
|
||||||
for (output, input) in ordered_stages.iter().zip(ordered_stages.iter().skip(1)) {
|
for (output, input) in ordered_stages.iter().zip(ordered_stages.iter().skip(1)) {
|
||||||
if let Err(err) = (input.entry_point.info().input_interface)
|
let out_spirv = output.entry_point.module().spirv();
|
||||||
.matches(&output.entry_point.info().output_interface)
|
let (out_execution_model, out_interface) =
|
||||||
{
|
match out_spirv.function(output.entry_point.id()).entry_point() {
|
||||||
return Err(Box::new(ValidationError {
|
Some(&Instruction::EntryPoint {
|
||||||
context: "stages".into(),
|
execution_model,
|
||||||
problem: format!(
|
ref interface,
|
||||||
"the output interface of the `ShaderStage::{:?}` stage does not \
|
..
|
||||||
match the input interface of the `ShaderStage::{:?}` stage: {}",
|
}) => (execution_model, interface),
|
||||||
ShaderStage::from(output.entry_point.info().execution_model),
|
_ => unreachable!(),
|
||||||
ShaderStage::from(input.entry_point.info().execution_model),
|
};
|
||||||
err
|
|
||||||
|
let in_spirv = input.entry_point.module().spirv();
|
||||||
|
let (in_execution_model, in_interface) =
|
||||||
|
match in_spirv.function(input.entry_point.id()).entry_point() {
|
||||||
|
Some(&Instruction::EntryPoint {
|
||||||
|
execution_model,
|
||||||
|
ref interface,
|
||||||
|
..
|
||||||
|
}) => (execution_model, interface),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
validate_interfaces_compatible(
|
||||||
|
out_spirv,
|
||||||
|
out_execution_model,
|
||||||
|
out_interface,
|
||||||
|
in_spirv,
|
||||||
|
in_execution_model,
|
||||||
|
in_interface,
|
||||||
|
device.enabled_features().maintenance4,
|
||||||
)
|
)
|
||||||
.into(),
|
.map_err(|mut err| {
|
||||||
vuids: &[
|
err.context = "stages".into();
|
||||||
"VUID-VkGraphicsPipelineCreateInfo-pStages-00742",
|
err.problem = format!(
|
||||||
"VUID-VkGraphicsPipelineCreateInfo-None-04889",
|
"the output interface of the `{:?}` stage is not compatible with \
|
||||||
],
|
the input interface of the `{:?}` stage: {}",
|
||||||
..Default::default()
|
ShaderStage::from(out_execution_model),
|
||||||
}));
|
ShaderStage::from(in_execution_model),
|
||||||
}
|
err.problem
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
err
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VUID-VkGraphicsPipelineCreateInfo-layout-01688
|
// VUID-VkGraphicsPipelineCreateInfo-layout-01688
|
||||||
|
@ -8,15 +8,13 @@
|
|||||||
//! the CPU). Consequently it is a CPU-intensive operation that should be performed at
|
//! the CPU). Consequently it is a CPU-intensive operation that should be performed at
|
||||||
//! initialization or during a loading screen.
|
//! initialization or during a loading screen.
|
||||||
|
|
||||||
pub use self::{compute::ComputePipeline, graphics::GraphicsPipeline, layout::PipelineLayout};
|
pub use self::{
|
||||||
|
compute::ComputePipeline, graphics::GraphicsPipeline, layout::PipelineLayout, shader::*,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
device::{Device, DeviceOwned},
|
device::DeviceOwned,
|
||||||
macros::{vulkan_bitflags, vulkan_enum},
|
macros::{vulkan_bitflags, vulkan_enum},
|
||||||
shader::{
|
shader::DescriptorBindingRequirements,
|
||||||
spirv::{BuiltIn, Decoration, ExecutionMode, Id, Instruction},
|
|
||||||
DescriptorBindingRequirements, EntryPoint, ShaderStage,
|
|
||||||
},
|
|
||||||
Requires, RequiresAllOf, RequiresOneOf, ValidationError,
|
|
||||||
};
|
};
|
||||||
use ahash::HashMap;
|
use ahash::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -25,6 +23,7 @@ pub mod cache;
|
|||||||
pub mod compute;
|
pub mod compute;
|
||||||
pub mod graphics;
|
pub mod graphics;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
pub(crate) mod shader;
|
||||||
|
|
||||||
/// A trait for operations shared between pipeline types.
|
/// A trait for operations shared between pipeline types.
|
||||||
pub trait Pipeline: DeviceOwned {
|
pub trait Pipeline: DeviceOwned {
|
||||||
@ -290,651 +289,6 @@ vulkan_bitflags! {
|
|||||||
]),*/
|
]),*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies a single shader stage when creating a pipeline.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PipelineShaderStageCreateInfo {
|
|
||||||
/// Additional properties of the shader stage.
|
|
||||||
///
|
|
||||||
/// The default value is empty.
|
|
||||||
pub flags: PipelineShaderStageCreateFlags,
|
|
||||||
|
|
||||||
/// The shader entry point for the stage, which includes any specialization constants.
|
|
||||||
///
|
|
||||||
/// There is no default value.
|
|
||||||
pub entry_point: EntryPoint,
|
|
||||||
|
|
||||||
/// The required subgroup size.
|
|
||||||
///
|
|
||||||
/// Requires [`subgroup_size_control`](crate::device::Features::subgroup_size_control). The
|
|
||||||
/// shader stage must be included in
|
|
||||||
/// [`required_subgroup_size_stages`](crate::device::Properties::required_subgroup_size_stages).
|
|
||||||
/// Subgroup size must be power of 2 and within
|
|
||||||
/// [`min_subgroup_size`](crate::device::Properties::min_subgroup_size)
|
|
||||||
/// and [`max_subgroup_size`](crate::device::Properties::max_subgroup_size).
|
|
||||||
///
|
|
||||||
/// For compute shaders, `max_compute_workgroup_subgroups * required_subgroup_size` must be
|
|
||||||
/// greater than or equal to `workgroup_size.x * workgroup_size.y * workgroup_size.z`.
|
|
||||||
///
|
|
||||||
/// The default value is None.
|
|
||||||
pub required_subgroup_size: Option<u32>,
|
|
||||||
|
|
||||||
pub _ne: crate::NonExhaustive,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PipelineShaderStageCreateInfo {
|
|
||||||
/// Returns a `PipelineShaderStageCreateInfo` with the specified `entry_point`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(entry_point: EntryPoint) -> Self {
|
|
||||||
Self {
|
|
||||||
flags: PipelineShaderStageCreateFlags::empty(),
|
|
||||||
entry_point,
|
|
||||||
required_subgroup_size: None,
|
|
||||||
_ne: crate::NonExhaustive(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
|
|
||||||
let &Self {
|
|
||||||
flags,
|
|
||||||
ref entry_point,
|
|
||||||
required_subgroup_size,
|
|
||||||
_ne: _,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
let properties = device.physical_device().properties();
|
|
||||||
|
|
||||||
flags.validate_device(device).map_err(|err| {
|
|
||||||
err.add_context("flags")
|
|
||||||
.set_vuids(&["VUID-VkPipelineShaderStageCreateInfo-flags-parameter"])
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let entry_point_info = entry_point.info();
|
|
||||||
let stage_enum = ShaderStage::from(entry_point_info.execution_model);
|
|
||||||
|
|
||||||
stage_enum.validate_device(device).map_err(|err| {
|
|
||||||
err.add_context("entry_point.info().execution")
|
|
||||||
.set_vuids(&["VUID-VkPipelineShaderStageCreateInfo-stage-parameter"])
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-pName-00707
|
|
||||||
// Guaranteed by definition of `EntryPoint`.
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-maxClipDistances-00708
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-maxCullDistances-00709
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-maxCombinedClipAndCullDistances-00710
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-maxSampleMaskWords-00711
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-stage-02596
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-stage-02597
|
|
||||||
|
|
||||||
match stage_enum {
|
|
||||||
ShaderStage::Vertex => {
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-stage-00712
|
|
||||||
// TODO:
|
|
||||||
}
|
|
||||||
ShaderStage::TessellationControl | ShaderStage::TessellationEvaluation => {
|
|
||||||
if !device.enabled_features().tessellation_shader {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "specifies a `ShaderStage::TessellationControl` or \
|
|
||||||
`ShaderStage::TessellationEvaluation` entry point"
|
|
||||||
.into(),
|
|
||||||
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
|
||||||
"tessellation_shader",
|
|
||||||
)])]),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-stage-00705"],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-stage-00713
|
|
||||||
// TODO:
|
|
||||||
}
|
|
||||||
ShaderStage::Geometry => {
|
|
||||||
if !device.enabled_features().geometry_shader {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "specifies a `ShaderStage::Geometry` entry point".into(),
|
|
||||||
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
|
||||||
"geometry_shader",
|
|
||||||
)])]),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-stage-00704"],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-stage-00714
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-stage-00715
|
|
||||||
}
|
|
||||||
ShaderStage::Fragment => {
|
|
||||||
// TODO:
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-stage-00718
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-stage-06685
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-stage-06686
|
|
||||||
}
|
|
||||||
ShaderStage::Compute => (),
|
|
||||||
ShaderStage::Raygen => (),
|
|
||||||
ShaderStage::AnyHit => (),
|
|
||||||
ShaderStage::ClosestHit => (),
|
|
||||||
ShaderStage::Miss => (),
|
|
||||||
ShaderStage::Intersection => (),
|
|
||||||
ShaderStage::Callable => (),
|
|
||||||
ShaderStage::Task => {
|
|
||||||
if !device.enabled_features().task_shader {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "specifies a `ShaderStage::Task` entry point".into(),
|
|
||||||
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
|
||||||
"task_shader",
|
|
||||||
)])]),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-stage-02092"],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ShaderStage::Mesh => {
|
|
||||||
if !device.enabled_features().mesh_shader {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "specifies a `ShaderStage::Mesh` entry point".into(),
|
|
||||||
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
|
||||||
"mesh_shader",
|
|
||||||
)])]),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-stage-02091"],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ShaderStage::SubpassShading => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
let spirv = entry_point.module().spirv();
|
|
||||||
let entry_point_function = spirv.function(entry_point.id());
|
|
||||||
|
|
||||||
let mut clip_distance_array_size = 0;
|
|
||||||
let mut cull_distance_array_size = 0;
|
|
||||||
|
|
||||||
for instruction in spirv.decorations() {
|
|
||||||
if let Instruction::Decorate {
|
|
||||||
target,
|
|
||||||
decoration: Decoration::BuiltIn { built_in },
|
|
||||||
} = *instruction
|
|
||||||
{
|
|
||||||
let variable_array_size = |variable| {
|
|
||||||
let result_type_id = match *spirv.id(variable).instruction() {
|
|
||||||
Instruction::Variable { result_type_id, .. } => result_type_id,
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let length = match *spirv.id(result_type_id).instruction() {
|
|
||||||
Instruction::TypeArray { length, .. } => length,
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let value = match *spirv.id(length).instruction() {
|
|
||||||
Instruction::Constant { ref value, .. } => {
|
|
||||||
if value.len() > 1 {
|
|
||||||
u32::MAX
|
|
||||||
} else {
|
|
||||||
value[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(value)
|
|
||||||
};
|
|
||||||
|
|
||||||
match built_in {
|
|
||||||
BuiltIn::ClipDistance => {
|
|
||||||
clip_distance_array_size = variable_array_size(target).unwrap();
|
|
||||||
|
|
||||||
if clip_distance_array_size > properties.max_clip_distances {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "the number of elements in the `ClipDistance` built-in \
|
|
||||||
variable is greater than the \
|
|
||||||
`max_clip_distances` device limit"
|
|
||||||
.into(),
|
|
||||||
vuids: &[
|
|
||||||
"VUID-VkPipelineShaderStageCreateInfo-maxClipDistances-00708",
|
|
||||||
],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BuiltIn::CullDistance => {
|
|
||||||
cull_distance_array_size = variable_array_size(target).unwrap();
|
|
||||||
|
|
||||||
if cull_distance_array_size > properties.max_cull_distances {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "the number of elements in the `CullDistance` built-in \
|
|
||||||
variable is greater than the \
|
|
||||||
`max_cull_distances` device limit"
|
|
||||||
.into(),
|
|
||||||
vuids: &[
|
|
||||||
"VUID-VkPipelineShaderStageCreateInfo-maxCullDistances-00709",
|
|
||||||
],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BuiltIn::SampleMask => {
|
|
||||||
if variable_array_size(target).unwrap() > properties.max_sample_mask_words {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "the number of elements in the `SampleMask` built-in \
|
|
||||||
variable is greater than the \
|
|
||||||
`max_sample_mask_words` device limit"
|
|
||||||
.into(),
|
|
||||||
vuids: &[
|
|
||||||
"VUID-VkPipelineShaderStageCreateInfo-maxSampleMaskWords-00711",
|
|
||||||
],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if clip_distance_array_size
|
|
||||||
.checked_add(cull_distance_array_size)
|
|
||||||
.map_or(true, |sum| {
|
|
||||||
sum > properties.max_combined_clip_and_cull_distances
|
|
||||||
})
|
|
||||||
{
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "the sum of the number of elements in the `ClipDistance` and \
|
|
||||||
`CullDistance` built-in variables is greater than the \
|
|
||||||
`max_combined_clip_and_cull_distances` device limit"
|
|
||||||
.into(),
|
|
||||||
vuids: &[
|
|
||||||
"VUID-VkPipelineShaderStageCreateInfo-maxCombinedClipAndCullDistances-00710",
|
|
||||||
],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for instruction in entry_point_function.execution_modes() {
|
|
||||||
if let Instruction::ExecutionMode {
|
|
||||||
mode: ExecutionMode::OutputVertices { vertex_count },
|
|
||||||
..
|
|
||||||
} = *instruction
|
|
||||||
{
|
|
||||||
match stage_enum {
|
|
||||||
ShaderStage::TessellationControl | ShaderStage::TessellationEvaluation => {
|
|
||||||
if vertex_count == 0 {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "the `vertex_count` of the \
|
|
||||||
`ExecutionMode::OutputVertices` is zero"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-stage-00713"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if vertex_count > properties.max_tessellation_patch_size {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "the `vertex_count` of the \
|
|
||||||
`ExecutionMode::OutputVertices` is greater than the \
|
|
||||||
`max_tessellation_patch_size` device limit"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-stage-00713"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ShaderStage::Geometry => {
|
|
||||||
if vertex_count == 0 {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "the `vertex_count` of the \
|
|
||||||
`ExecutionMode::OutputVertices` is zero"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-stage-00714"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if vertex_count > properties.max_geometry_output_vertices {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "entry_point".into(),
|
|
||||||
problem: "the `vertex_count` of the \
|
|
||||||
`ExecutionMode::OutputVertices` is greater than the \
|
|
||||||
`max_geometry_output_vertices` device limit"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-stage-00714"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let local_size = (spirv
|
|
||||||
.decorations()
|
|
||||||
.iter()
|
|
||||||
.find_map(|instruction| match *instruction {
|
|
||||||
Instruction::Decorate {
|
|
||||||
target,
|
|
||||||
decoration:
|
|
||||||
Decoration::BuiltIn {
|
|
||||||
built_in: BuiltIn::WorkgroupSize,
|
|
||||||
},
|
|
||||||
} => {
|
|
||||||
let constituents: &[Id; 3] = match *spirv.id(target).instruction() {
|
|
||||||
Instruction::ConstantComposite {
|
|
||||||
ref constituents, ..
|
|
||||||
} => constituents.as_slice().try_into().unwrap(),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let local_size = constituents.map(|id| match *spirv.id(id).instruction() {
|
|
||||||
Instruction::Constant { ref value, .. } => {
|
|
||||||
assert!(value.len() == 1);
|
|
||||||
value[0]
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
});
|
|
||||||
|
|
||||||
Some(local_size)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}))
|
|
||||||
.or_else(|| {
|
|
||||||
entry_point_function
|
|
||||||
.execution_modes()
|
|
||||||
.iter()
|
|
||||||
.find_map(|instruction| match *instruction {
|
|
||||||
Instruction::ExecutionMode {
|
|
||||||
mode:
|
|
||||||
ExecutionMode::LocalSize {
|
|
||||||
x_size,
|
|
||||||
y_size,
|
|
||||||
z_size,
|
|
||||||
},
|
|
||||||
..
|
|
||||||
} => Some([x_size, y_size, z_size]),
|
|
||||||
Instruction::ExecutionModeId {
|
|
||||||
mode:
|
|
||||||
ExecutionMode::LocalSizeId {
|
|
||||||
x_size,
|
|
||||||
y_size,
|
|
||||||
z_size,
|
|
||||||
},
|
|
||||||
..
|
|
||||||
} => Some([x_size, y_size, z_size].map(
|
|
||||||
|id| match *spirv.id(id).instruction() {
|
|
||||||
Instruction::Constant { ref value, .. } => {
|
|
||||||
assert!(value.len() == 1);
|
|
||||||
value[0]
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
let workgroup_size = local_size.into_iter().try_fold(1, u32::checked_mul);
|
|
||||||
|
|
||||||
match stage_enum {
|
|
||||||
ShaderStage::Compute => {
|
|
||||||
if local_size[0] > properties.max_compute_work_group_size[0] {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the `local_size_x` of `entry_point` is greater than \
|
|
||||||
`max_compute_work_group_size[0]`"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-x-06429"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if local_size[1] > properties.max_compute_work_group_size[1] {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the `local_size_y` of `entry_point` is greater than \
|
|
||||||
`max_compute_work_group_size[1]`"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-x-06430"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if local_size[2] > properties.max_compute_work_group_size[2] {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the `local_size_x` of `entry_point` is greater than \
|
|
||||||
`max_compute_work_group_size[2]`"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-x-06431"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if workgroup_size.map_or(true, |size| {
|
|
||||||
size > properties.max_compute_work_group_invocations
|
|
||||||
}) {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the product of the `local_size_x`, `local_size_y` and \
|
|
||||||
`local_size_z` of `entry_point` is greater than the \
|
|
||||||
`max_compute_work_group_invocations` device limit"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-x-06432"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ShaderStage::Task => {
|
|
||||||
if local_size[0] > properties.max_task_work_group_size.unwrap_or_default()[0] {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the `local_size_x` of `entry_point` is greater than \
|
|
||||||
`max_task_work_group_size[0]`"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-TaskEXT-07291"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if local_size[1] > properties.max_task_work_group_size.unwrap_or_default()[1] {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the `local_size_y` of `entry_point` is greater than \
|
|
||||||
`max_task_work_group_size[1]`"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-TaskEXT-07292"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if local_size[2] > properties.max_task_work_group_size.unwrap_or_default()[2] {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the `local_size_x` of `entry_point` is greater than \
|
|
||||||
`max_task_work_group_size[2]`"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-TaskEXT-07293"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if workgroup_size.map_or(true, |size| {
|
|
||||||
size > properties
|
|
||||||
.max_task_work_group_invocations
|
|
||||||
.unwrap_or_default()
|
|
||||||
}) {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the product of the `local_size_x`, `local_size_y` and \
|
|
||||||
`local_size_z` of `entry_point` is greater than the \
|
|
||||||
`max_task_work_group_invocations` device limit"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-TaskEXT-07294"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ShaderStage::Mesh => {
|
|
||||||
if local_size[0] > properties.max_mesh_work_group_size.unwrap_or_default()[0] {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the `local_size_x` of `entry_point` is greater than \
|
|
||||||
`max_mesh_work_group_size[0]`"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-MeshEXT-07295"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if local_size[1] > properties.max_mesh_work_group_size.unwrap_or_default()[1] {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the `local_size_y` of `entry_point` is greater than \
|
|
||||||
`max_mesh_work_group_size[1]`"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-MeshEXT-07296"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if local_size[2] > properties.max_mesh_work_group_size.unwrap_or_default()[2] {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the `local_size_x` of `entry_point` is greater than \
|
|
||||||
`max_mesh_work_group_size[2]`"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-MeshEXT-07297"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if workgroup_size.map_or(true, |size| {
|
|
||||||
size > properties
|
|
||||||
.max_mesh_work_group_invocations
|
|
||||||
.unwrap_or_default()
|
|
||||||
}) {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the product of the `local_size_x`, `local_size_y` and \
|
|
||||||
`local_size_z` of `entry_point` is greater than the \
|
|
||||||
`max_mesh_work_group_invocations` device limit"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-RuntimeSpirv-MeshEXT-07298"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
let workgroup_size = workgroup_size.unwrap();
|
|
||||||
|
|
||||||
if let Some(required_subgroup_size) = required_subgroup_size {
|
|
||||||
if !device.enabled_features().subgroup_size_control {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "required_subgroup_size".into(),
|
|
||||||
problem: "is `Some`".into(),
|
|
||||||
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
|
|
||||||
"subgroup_size_control",
|
|
||||||
)])]),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-pNext-02755"],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !properties
|
|
||||||
.required_subgroup_size_stages
|
|
||||||
.unwrap_or_default()
|
|
||||||
.contains_enum(stage_enum)
|
|
||||||
{
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "`required_subgroup_size` is `Some`, but the \
|
|
||||||
`required_subgroup_size_stages` device property does not contain the \
|
|
||||||
shader stage of `entry_point`"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-pNext-02755"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !required_subgroup_size.is_power_of_two() {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "required_subgroup_size".into(),
|
|
||||||
problem: "is not a power of 2".into(),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageRequiredSubgroupSizeCreateInfo-requiredSubgroupSize-02760"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if required_subgroup_size < properties.min_subgroup_size.unwrap_or(1) {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "required_subgroup_size".into(),
|
|
||||||
problem: "is less than the `min_subgroup_size` device limit".into(),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageRequiredSubgroupSizeCreateInfo-requiredSubgroupSize-02761"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if required_subgroup_size > properties.max_subgroup_size.unwrap_or(128) {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
context: "required_subgroup_size".into(),
|
|
||||||
problem: "is greater than the `max_subgroup_size` device limit".into(),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageRequiredSubgroupSizeCreateInfo-requiredSubgroupSize-02762"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches!(
|
|
||||||
stage_enum,
|
|
||||||
ShaderStage::Compute | ShaderStage::Mesh | ShaderStage::Task
|
|
||||||
) && workgroup_size
|
|
||||||
> required_subgroup_size
|
|
||||||
.checked_mul(
|
|
||||||
properties
|
|
||||||
.max_compute_workgroup_subgroups
|
|
||||||
.unwrap_or_default(),
|
|
||||||
)
|
|
||||||
.unwrap_or(u32::MAX)
|
|
||||||
{
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the product of the `local_size_x`, `local_size_y` and \
|
|
||||||
`local_size_z` of `entry_point` is greater than the the product \
|
|
||||||
of `required_subgroup_size` and the \
|
|
||||||
`max_compute_workgroup_subgroups` device limit"
|
|
||||||
.into(),
|
|
||||||
vuids: &["VUID-VkPipelineShaderStageCreateInfo-pNext-02756"],
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// VUID-VkPipelineShaderStageCreateInfo-module-08987
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vulkan_bitflags! {
|
|
||||||
#[non_exhaustive]
|
|
||||||
|
|
||||||
/// Flags specifying additional properties of a pipeline shader stage.
|
|
||||||
PipelineShaderStageCreateFlags = PipelineShaderStageCreateFlags(u32);
|
|
||||||
|
|
||||||
/* TODO: enable
|
|
||||||
// TODO: document
|
|
||||||
ALLOW_VARYING_SUBGROUP_SIZE = ALLOW_VARYING_SUBGROUP_SIZE
|
|
||||||
RequiresOneOf([
|
|
||||||
RequiresAllOf([APIVersion(V1_3)]),
|
|
||||||
RequiresAllOf([DeviceExtension(ext_subgroup_size_control)]),
|
|
||||||
]),
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* TODO: enable
|
|
||||||
// TODO: document
|
|
||||||
REQUIRE_FULL_SUBGROUPS = REQUIRE_FULL_SUBGROUPS
|
|
||||||
RequiresOneOf([
|
|
||||||
RequiresAllOf([APIVersion(V1_3)]),
|
|
||||||
RequiresAllOf([DeviceExtension(ext_subgroup_size_control)]),
|
|
||||||
]),
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
vulkan_enum! {
|
vulkan_enum! {
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
|
||||||
|
1366
vulkano/src/pipeline/shader.rs
Normal file
1366
vulkano/src/pipeline/shader.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1115,65 +1115,6 @@ impl ShaderInterface {
|
|||||||
pub fn elements(&self) -> &[ShaderInterfaceEntry] {
|
pub fn elements(&self) -> &[ShaderInterfaceEntry] {
|
||||||
self.elements.as_ref()
|
self.elements.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether the interface is potentially compatible with another one.
|
|
||||||
///
|
|
||||||
/// Returns `Ok` if the two interfaces are compatible.
|
|
||||||
#[inline]
|
|
||||||
pub fn matches(&self, other: &ShaderInterface) -> Result<(), Box<ValidationError>> {
|
|
||||||
if self.elements().len() != other.elements().len() {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: "the number of elements in the shader interfaces are not equal".into(),
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for a in self.elements() {
|
|
||||||
let location_range = a.location..a.location + a.ty.num_locations();
|
|
||||||
for loc in location_range {
|
|
||||||
let b = match other
|
|
||||||
.elements()
|
|
||||||
.iter()
|
|
||||||
.find(|e| loc >= e.location && loc < e.location + e.ty.num_locations())
|
|
||||||
{
|
|
||||||
None => {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: format!(
|
|
||||||
"the second shader is missing an interface element at location {}",
|
|
||||||
loc
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
Some(b) => b,
|
|
||||||
};
|
|
||||||
|
|
||||||
if a.ty != b.ty {
|
|
||||||
return Err(Box::new(ValidationError {
|
|
||||||
problem: format!(
|
|
||||||
"the interface element at location {} does not have the same type \
|
|
||||||
in both shaders",
|
|
||||||
loc
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: enforce this?
|
|
||||||
/*match (a.name, b.name) {
|
|
||||||
(Some(ref an), Some(ref bn)) => if an != bn { return false },
|
|
||||||
_ => ()
|
|
||||||
};*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: since we check that the number of elements is the same, we don't need to iterate
|
|
||||||
// over b's elements.
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Entry of a shader interface definition.
|
/// Entry of a shader interface definition.
|
||||||
|
Loading…
Reference in New Issue
Block a user