Add support for the ext_mesh_shader extension (#2433)

* Add support for the `ext_mesh_shader` extension

* Helper function for queries

* Documentation update

* Add mesh primitives generated query

* Derp

* Support in vulkano-shaders too

* More doc fixes

* Better docs, explicitly saying when the values must be `Some` or `None`.

* Update vulkano/src/pipeline/graphics/mod.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:
Rua 2023-12-28 19:44:22 +01:00 committed by GitHub
parent b2bbe34896
commit 655ca5e6c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1538 additions and 468 deletions

View File

@ -92,10 +92,12 @@
//! of the following: //! of the following:
//! //!
//! - `vertex` //! - `vertex`
//! - `fragment`
//! - `geometry`
//! - `tess_ctrl` //! - `tess_ctrl`
//! - `tess_eval` //! - `tess_eval`
//! - `geometry`
//! - `task`
//! - `mesh`
//! - `fragment`
//! - `compute` //! - `compute`
//! - `raygen` //! - `raygen`
//! - `anyhit` //! - `anyhit`
@ -421,10 +423,12 @@ impl Parse for MacroInput {
output.0 = Some(match lit.value().as_str() { output.0 = Some(match lit.value().as_str() {
"vertex" => ShaderKind::Vertex, "vertex" => ShaderKind::Vertex,
"fragment" => ShaderKind::Fragment,
"geometry" => ShaderKind::Geometry,
"tess_ctrl" => ShaderKind::TessControl, "tess_ctrl" => ShaderKind::TessControl,
"tess_eval" => ShaderKind::TessEvaluation, "tess_eval" => ShaderKind::TessEvaluation,
"geometry" => ShaderKind::Geometry,
"task" => ShaderKind::Task,
"mesh" => ShaderKind::Mesh,
"fragment" => ShaderKind::Fragment,
"compute" => ShaderKind::Compute, "compute" => ShaderKind::Compute,
"raygen" => ShaderKind::RayGeneration, "raygen" => ShaderKind::RayGeneration,
"anyhit" => ShaderKind::AnyHit, "anyhit" => ShaderKind::AnyHit,
@ -434,9 +438,9 @@ impl Parse for MacroInput {
"callable" => ShaderKind::Callable, "callable" => ShaderKind::Callable,
ty => bail!( ty => bail!(
lit, lit,
"expected `vertex`, `fragment`, `geometry`, `tess_ctrl`, `tess_eval`, \ "expected `vertex`, `tess_ctrl`, `tess_eval`, `geometry`, `task`, \
`compute`, `raygen`, `anyhit`, `closesthit`, `miss`, `intersection` or \ `mesh`, `fragment` `compute`, `raygen`, `anyhit`, `closesthit`, \
`callable`, found `{ty}`", `miss`, `intersection` or `callable`, found `{ty}`",
), ),
}); });
} }

File diff suppressed because it is too large Load Diff

View File

@ -505,6 +505,22 @@ impl RawRecordingCommandBuffer {
})); }));
} }
} }
QueryType::MeshPrimitivesGenerated => {
if !queue_family_properties
.queue_flags
.intersects(QueueFlags::GRAPHICS)
{
return Err(Box::new(ValidationError {
problem: "`query_pool.query_type()` is \
`QueryType::MeshPrimitivesGenerated`, but \
the queue family of the command buffer does not support \
graphics operations"
.into(),
vuids: &["VUID-vkCmdBeginQuery-queryType-07070"],
..Default::default()
}));
}
}
QueryType::Timestamp QueryType::Timestamp
| QueryType::AccelerationStructureCompactedSize | QueryType::AccelerationStructureCompactedSize
| QueryType::AccelerationStructureSerializationSize | QueryType::AccelerationStructureSerializationSize

View File

@ -172,6 +172,31 @@ pub struct DrawIndirectCommand {
pub first_instance: u32, pub first_instance: u32,
} }
/// Used as buffer contents to provide input for the
/// [`RecordingCommandBuffer::draw_mesh_tasks_indirect`] command.
///
/// # Safety
///
/// - If the graphics pipeline **does not** include a task shader, then the
/// `group_count_x`, `group_count_y` and `group_count_z` values must not be greater than the
/// respective elements of the
/// [`max_mesh_work_group_count`](Properties::max_mesh_work_group_count) device limit,
/// and the product of these three values must not be greater than the
/// [`max_mesh_work_group_total_count`](Properties::max_mesh_work_group_total_count) device limit.
/// - If the graphics pipeline **does** include a task shader, then the
/// `group_count_x`, `group_count_y` and `group_count_z` values must not be greater than the
/// respective elements of the
/// [`max_task_work_group_count`](Properties::max_task_work_group_count) device limit,
/// and the product of these three values must not be greater than the
/// [`max_task_work_group_total_count`](Properties::max_task_work_group_total_count) device limit.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod, PartialEq, Eq)]
pub struct DrawMeshTasksIndirectCommand {
pub group_count_x: u32,
pub group_count_y: u32,
pub group_count_z: u32,
}
/// Used as buffer contents to provide input for the /// Used as buffer contents to provide input for the
/// [`RecordingCommandBuffer::draw_indexed_indirect`] command. /// [`RecordingCommandBuffer::draw_indexed_indirect`] command.
/// ///

View File

@ -2,11 +2,27 @@
//! //!
//! Unlike a compute pipeline, which performs general-purpose work, a graphics pipeline is geared //! Unlike a compute pipeline, which performs general-purpose work, a graphics pipeline is geared
//! specifically towards doing graphical processing. To that end, it consists of several shaders, //! specifically towards doing graphical processing. To that end, it consists of several shaders,
//! with additional state and glue logic in between. //! with additional state and glue logic in between, known as the pipeline's *state*.
//! The state often directly corresponds to one or more steps in the graphics pipeline. Each
//! state collection has a dedicated submodule.
//!
//! # Processing steps
//! //!
//! A graphics pipeline performs many separate steps, that execute more or less in sequence. //! A graphics pipeline performs many separate steps, that execute more or less in sequence.
//! Due to the parallel nature of a GPU, no strict ordering guarantees may exist. //! Due to the parallel nature of a GPU, no strict ordering guarantees may exist.
//! //!
//! Graphics pipelines come in two different forms:
//! - *Primitive shading* graphics pipelines, which contain a vertex shader, vertex input and
//! input assembly state, and optionally tessellation shaders and/or a geometry shader.
//! - *Mesh shading* graphics pipelines, which contain a mesh shader, and optionally a
//! task shader.
//!
//! These types differ in the operations that are performed in the first half of the pipeline,
//! but share a common second half. The type of a graphics pipeline is determined by whether
//! it contains a vertex or a mesh shader (it cannot contain both).
//!
//! ## Primitive shading
//!
//! 1. Vertex input and assembly: vertex input data is read from data buffers and then assembled //! 1. Vertex input and assembly: vertex input data is read from data buffers and then assembled
//! into primitives (points, lines, triangles etc.). //! into primitives (points, lines, triangles etc.).
//! 2. Vertex shader invocations: the vertex data of each primitive is fed as input to the vertex //! 2. Vertex shader invocations: the vertex data of each primitive is fed as input to the vertex
@ -17,14 +33,31 @@
//! newly created vertices. //! newly created vertices.
//! 4. (Optional) Geometry shading: whole primitives are fed as input and processed into a new set //! 4. (Optional) Geometry shading: whole primitives are fed as input and processed into a new set
//! of output primitives. //! of output primitives.
//! 5. Vertex post-processing, including: //!
//! ## Mesh shading
//!
//! 1. (Optional) Task shader invocations: the task shader is run once for each workgroup in the
//! draw command. The task shader then spawns one or more mesh shader invocations.
//! 2. Mesh shader invocations: the mesh shader is run, either once each time it is spawned by a
//! task shader, or if there is no task shader, once for each workgroup in the draw command.
//! The mesh shader outputs a list of primitives (triangles etc).
//!
//! Mesh shading pipelines do not receive any vertex input; their input data is supplied entirely
//! from resources bound via descriptor sets, in combination with the x, y and z coordinates of
//! the current workgroup.
//!
//! ## Rasterization, fragment processing and output
//!
//! These steps are shared by all graphics pipeline types.
//!
//! 1. Vertex post-processing, including:
//! - Clipping primitives to the view frustum and user-defined clipping planes. //! - Clipping primitives to the view frustum and user-defined clipping planes.
//! - Perspective division. //! - Perspective division.
//! - Viewport mapping. //! - Viewport mapping.
//! 6. Rasterization: converting primitives into a two-dimensional representation. Primitives may be //! 2. Rasterization: converting primitives into a two-dimensional representation. Primitives may be
//! discarded depending on their orientation, and are then converted into a collection of //! discarded depending on their orientation, and are then converted into a collection of
//! fragments that are processed further. //! fragments that are processed further.
//! 7. Fragment operations. These include invocations of the fragment shader, which generates the //! 3. Fragment operations. These include invocations of the fragment shader, which generates the
//! values to be written to the color attachment. Various testing and discarding operations can //! values to be written to the color attachment. Various testing and discarding operations can
//! be performed both before and after the fragment shader ("early" and "late" fragment tests), //! be performed both before and after the fragment shader ("early" and "late" fragment tests),
//! including: //! including:
@ -34,13 +67,11 @@
//! - Depth bounds test //! - Depth bounds test
//! - Stencil test //! - Stencil test
//! - Depth test //! - Depth test
//! 8. Color attachment output: the final pixel data is written to a framebuffer. Blending and //! 4. Color attachment output: the final pixel data is written to a framebuffer. Blending and
//! logical operations can be applied to combine incoming pixel data with data already present //! logical operations can be applied to combine incoming pixel data with data already present
//! in the framebuffer. //! in the framebuffer.
//! //!
//! A graphics pipeline contains many configuration options, which are grouped into collections of //! # Using a graphics pipeline
//! "state". Often, these directly correspond to one or more steps in the graphics pipeline. Each
//! state collection has a dedicated submodule.
//! //!
//! Once a graphics pipeline has been created, you can execute it by first *binding* it in a command //! Once a graphics pipeline has been created, you can execute it by first *binding* it in a command
//! buffer, binding the necessary vertex buffers, binding any descriptor sets, setting push //! buffer, binding the necessary vertex buffers, binding any descriptor sets, setting push
@ -117,10 +148,9 @@ pub struct GraphicsPipeline {
id: NonZeroU64, id: NonZeroU64,
flags: PipelineCreateFlags, flags: PipelineCreateFlags,
// TODO: replace () with an object that describes the shaders in some way. shader_stages: ShaderStages,
shaders: HashMap<ShaderStage, ()>,
vertex_input_state: Option<VertexInputState>, vertex_input_state: Option<VertexInputState>,
input_assembly_state: InputAssemblyState, input_assembly_state: Option<InputAssemblyState>,
tessellation_state: Option<TessellationState>, tessellation_state: Option<TessellationState>,
viewport_state: Option<ViewportState>, viewport_state: Option<ViewportState>,
rasterization_state: RasterizationState, rasterization_state: RasterizationState,
@ -137,6 +167,7 @@ pub struct GraphicsPipeline {
num_used_descriptor_sets: u32, num_used_descriptor_sets: u32,
fixed_state: HashSet<DynamicState>, fixed_state: HashSet<DynamicState>,
fragment_tests_stages: Option<FragmentTestsStages>, fragment_tests_stages: Option<FragmentTestsStages>,
mesh_is_nv: bool,
// Note: this is only `Some` if `vertex_input_state` is `None`. // Note: this is only `Some` if `vertex_input_state` is `None`.
required_vertex_inputs: Option<HashMap<u32, ShaderInterfaceLocationInfo>>, required_vertex_inputs: Option<HashMap<u32, ShaderInterfaceLocationInfo>>,
} }
@ -917,7 +948,8 @@ impl GraphicsPipeline {
_ne: _, _ne: _,
} = create_info; } = create_info;
let mut shaders = HashMap::default(); let mut shader_stages = ShaderStages::empty();
let mut mesh_is_nv = false;
let mut descriptor_binding_requirements: HashMap< let mut descriptor_binding_requirements: HashMap<
(u32, u32), (u32, u32),
DescriptorBindingRequirements, DescriptorBindingRequirements,
@ -932,7 +964,7 @@ impl GraphicsPipeline {
let entry_point_info = entry_point.info(); let entry_point_info = entry_point.info();
let stage = ShaderStage::from(entry_point_info.execution_model); let stage = ShaderStage::from(entry_point_info.execution_model);
shaders.insert(stage, ()); shader_stages |= stage.into();
let spirv = entry_point.module().spirv(); let spirv = entry_point.module().spirv();
let entry_point_function = spirv.function(entry_point.id()); let entry_point_function = spirv.function(entry_point.id());
@ -947,6 +979,7 @@ impl GraphicsPipeline {
)); ));
} }
} }
ExecutionModel::MeshNV | ExecutionModel::TaskNV => mesh_is_nv = true,
ExecutionModel::Fragment => { ExecutionModel::Fragment => {
fragment_tests_stages = Some(FragmentTestsStages::Late); fragment_tests_stages = Some(FragmentTestsStages::Late);
@ -1055,9 +1088,9 @@ impl GraphicsPipeline {
id: Self::next_id(), id: Self::next_id(),
flags, flags,
shaders, shader_stages,
vertex_input_state, vertex_input_state,
input_assembly_state: input_assembly_state.unwrap(), // Can be None if there's a mesh shader, but we don't support that yet input_assembly_state,
tessellation_state, tessellation_state,
viewport_state, viewport_state,
rasterization_state: rasterization_state.unwrap(), // Can be None for pipeline libraries, but we don't support that yet rasterization_state: rasterization_state.unwrap(), // Can be None for pipeline libraries, but we don't support that yet
@ -1074,6 +1107,7 @@ impl GraphicsPipeline {
num_used_descriptor_sets, num_used_descriptor_sets,
fixed_state, fixed_state,
fragment_tests_stages, fragment_tests_stages,
mesh_is_nv,
required_vertex_inputs, required_vertex_inputs,
}) })
} }
@ -1090,16 +1124,15 @@ impl GraphicsPipeline {
self.flags self.flags
} }
/// Returns information about a particular shader. /// Returns the shader stages that this pipeline contains.
///
/// `None` is returned if the pipeline does not contain this shader.
///
/// Compatibility note: `()` is temporary, it will be replaced with something else in the
/// future.
// TODO: ^ implement and make this public
#[inline] #[inline]
pub(crate) fn shader(&self, stage: ShaderStage) -> Option<()> { pub fn shader_stages(&self) -> ShaderStages {
self.shaders.get(&stage).copied() self.shader_stages
}
#[inline]
pub(crate) fn mesh_is_nv(&self) -> bool {
self.mesh_is_nv
} }
/// Returns the vertex input state used to create this pipeline. /// Returns the vertex input state used to create this pipeline.
@ -1110,8 +1143,8 @@ impl GraphicsPipeline {
/// Returns the input assembly state used to create this pipeline. /// Returns the input assembly state used to create this pipeline.
#[inline] #[inline]
pub fn input_assembly_state(&self) -> &InputAssemblyState { pub fn input_assembly_state(&self) -> Option<&InputAssemblyState> {
&self.input_assembly_state self.input_assembly_state.as_ref()
} }
/// Returns the tessellation state used to create this pipeline. /// Returns the tessellation state used to create this pipeline.
@ -1251,35 +1284,39 @@ pub struct GraphicsPipelineCreateInfo {
/// The shader stages to use. /// The shader stages to use.
/// ///
/// A vertex shader must always be included. Other stages are optional. /// Either a vertex shader or mesh shader must always be included. Other stages are optional.
/// ///
/// The default value is empty. /// The default value is empty.
pub stages: SmallVec<[PipelineShaderStageCreateInfo; 5]>, pub stages: SmallVec<[PipelineShaderStageCreateInfo; 5]>,
/// The vertex input state. /// The vertex input state.
/// ///
/// This state is always used, and must be provided. /// This must be `Some` if `stages` contains a vertex shader.
/// It must be `None` otherwise.
/// ///
/// The default value is `None`. /// The default value is `None`.
pub vertex_input_state: Option<VertexInputState>, pub vertex_input_state: Option<VertexInputState>,
/// The input assembly state. /// The input assembly state.
/// ///
/// This state is always used, and must be provided. /// This must be `Some` if `stages` contains a vertex shader.
/// It must be `None` otherwise.
/// ///
/// The default value is `None`. /// The default value is `None`.
pub input_assembly_state: Option<InputAssemblyState>, pub input_assembly_state: Option<InputAssemblyState>,
/// The tessellation state. /// The tessellation state.
/// ///
/// This state is used if `stages` contains tessellation shaders. /// This must be `Some` if `stages` contains tessellation shaders.
/// It must be `None` otherwise.
/// ///
/// The default value is `None`. /// The default value is `None`.
pub tessellation_state: Option<TessellationState>, pub tessellation_state: Option<TessellationState>,
/// The viewport state. /// The viewport state.
/// ///
/// This state is used if [rasterizer discarding] is not enabled. /// This must be `Some` if [rasterizer discarding] is not enabled.
/// It must be `None` otherwise.
/// ///
/// The default value is `None`. /// The default value is `None`.
/// ///
@ -1288,14 +1325,15 @@ pub struct GraphicsPipelineCreateInfo {
/// The rasterization state. /// The rasterization state.
/// ///
/// This state is always used, and must be provided. /// This must always be `Some`.
/// ///
/// The default value is `None`. /// The default value is `None`.
pub rasterization_state: Option<RasterizationState>, pub rasterization_state: Option<RasterizationState>,
/// The multisample state. /// The multisample state.
/// ///
/// This state is used if [rasterizer discarding] is not enabled. /// This must be `Some` if [rasterizer discarding] is not enabled.
/// It must be `None` otherwise.
/// ///
/// The default value is `None`. /// The default value is `None`.
/// ///
@ -1304,8 +1342,9 @@ pub struct GraphicsPipelineCreateInfo {
/// The depth/stencil state. /// The depth/stencil state.
/// ///
/// This state is used if `render_pass` has depth/stencil attachments, or if /// This must be `Some` if `render_pass` has depth/stencil attachments, or if
/// [rasterizer discarding] is enabled. /// [rasterizer discarding] is enabled.
/// It must be `None` otherwise.
/// ///
/// The default value is `None`. /// The default value is `None`.
/// ///
@ -1314,8 +1353,9 @@ pub struct GraphicsPipelineCreateInfo {
/// The color blend state. /// The color blend state.
/// ///
/// This state is used if `render_pass` has color attachments, and [rasterizer discarding] is /// This must be `Some` if `render_pass` has color attachments, and [rasterizer discarding] is
/// not enabled. /// not enabled.
/// It must be `None` otherwise.
/// ///
/// The default value is `None`. /// The default value is `None`.
/// ///
@ -1350,8 +1390,6 @@ pub struct GraphicsPipelineCreateInfo {
/// The discard rectangle state. /// The discard rectangle state.
/// ///
/// This state is always used if it is provided.
///
/// The default value is `None`. /// The default value is `None`.
pub discard_rectangle_state: Option<DiscardRectangleState>, pub discard_rectangle_state: Option<DiscardRectangleState>,
@ -1448,17 +1486,17 @@ impl GraphicsPipelineCreateInfo {
Gather shader stages Gather shader stages
*/ */
let mut stages_present = ShaderStages::empty(); const PRIMITIVE_SHADING_STAGES: ShaderStages = ShaderStages::VERTEX
let mut vertex_stage = None; .union(ShaderStages::TESSELLATION_CONTROL)
let mut tessellation_control_stage = None; .union(ShaderStages::TESSELLATION_CONTROL)
let mut tessellation_evaluation_stage = None; .union(ShaderStages::GEOMETRY);
let mut geometry_stage = None; const MESH_SHADING_STAGES: ShaderStages = ShaderStages::MESH.union(ShaderStages::TASK);
let mut fragment_stage = None;
for (stage_index, stage) in stages.iter().enumerate() { let mut stages_present = ShaderStages::empty();
let entry_point_info = stage.entry_point.info();
let stage_enum = ShaderStage::from(entry_point_info.execution_model); for stage in stages {
let stage_flag = ShaderStages::from(stage_enum); let stage_flag =
ShaderStages::from(ShaderStage::from(stage.entry_point.info().execution_model));
if stages_present.intersects(stage_flag) { if stages_present.intersects(stage_flag) {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
@ -1474,43 +1512,6 @@ impl GraphicsPipelineCreateInfo {
})); }));
} }
const PRIMITIVE_SHADING_STAGES: ShaderStages = ShaderStages::VERTEX
.union(ShaderStages::TESSELLATION_CONTROL)
.union(ShaderStages::TESSELLATION_CONTROL)
.union(ShaderStages::GEOMETRY);
const MESH_SHADING_STAGES: ShaderStages = ShaderStages::MESH.union(ShaderStages::TASK);
if stage_flag.intersects(PRIMITIVE_SHADING_STAGES)
&& stages_present.intersects(MESH_SHADING_STAGES)
|| stage_flag.intersects(MESH_SHADING_STAGES)
&& stages_present.intersects(PRIMITIVE_SHADING_STAGES)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains both primitive shading stages and mesh shading stages"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-02095"],
..Default::default()
}));
}
let stage_slot = match stage_enum {
ShaderStage::Vertex => &mut vertex_stage,
ShaderStage::TessellationControl => &mut tessellation_control_stage,
ShaderStage::TessellationEvaluation => &mut tessellation_evaluation_stage,
ShaderStage::Geometry => &mut geometry_stage,
ShaderStage::Fragment => &mut fragment_stage,
_ => {
return Err(Box::new(ValidationError {
context: format!("stages[{}]", stage_index).into(),
problem: "is not a pre-rasterization or fragment shader stage".into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-06896"],
..Default::default()
}));
}
};
*stage_slot = Some(stage);
stages_present |= stage_flag; stages_present |= stage_flag;
} }
@ -1548,13 +1549,8 @@ impl GraphicsPipelineCreateInfo {
_ => (), _ => (),
} }
let need_vertex_input_state = need_pre_rasterization_shader_state let need_vertex_input_state =
&& stages.iter().any(|stage| { need_pre_rasterization_shader_state && stages_present.intersects(ShaderStages::VERTEX);
matches!(
stage.entry_point.info().execution_model,
ExecutionModel::Vertex
)
});
let need_fragment_shader_state = need_pre_rasterization_shader_state let need_fragment_shader_state = need_pre_rasterization_shader_state
&& (!rasterization_state && (!rasterization_state
.as_ref() .as_ref()
@ -1568,109 +1564,34 @@ impl GraphicsPipelineCreateInfo {
.rasterizer_discard_enable .rasterizer_discard_enable
|| dynamic_state.contains(&DynamicState::RasterizerDiscardEnable)); || dynamic_state.contains(&DynamicState::RasterizerDiscardEnable));
match (vertex_stage.is_some(), need_pre_rasterization_shader_state) { if need_pre_rasterization_shader_state {
(true, false) => { if !stages_present.intersects(ShaderStages::VERTEX | ShaderStages::MESH) {
return Err(Box::new(ValidationError {
problem: "the pipeline is not being created with \
pre-rasterization shader state, but `stages` contains a \
`ShaderStage::Vertex` stage"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-06895"],
..Default::default()
}));
}
(false, true) => {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
problem: "the pipeline is being created with \ problem: "the pipeline is being created with \
pre-rasterization shader state, but `stages` does not contain a \ pre-rasterization shader state, but `stages` does not contain a \
`ShaderStage::Vertex` stage" `ShaderStage::Vertex` or `ShaderStage::Mesh` stage"
.into(), .into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-stage-02096"], vuids: &["VUID-VkGraphicsPipelineCreateInfo-stage-02096"],
..Default::default() ..Default::default()
})); }));
} }
_ => (), } else {
} if stages_present.intersects(PRIMITIVE_SHADING_STAGES | MESH_SHADING_STAGES) {
match (
tessellation_control_stage.is_some(),
need_pre_rasterization_shader_state,
) {
(true, false) => {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
problem: "the pipeline is not being created with \ problem: "the pipeline is not being created with \
pre-rasterization shader state, but `stages` contains a \ pre-rasterization shader state, but `stages` contains a \
`ShaderStage::TessellationControl` stage" pre-rasterization shader stage"
.into(), .into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-06895"], vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-06895"],
..Default::default() ..Default::default()
})); }));
} }
(false, true) => (),
_ => (),
} }
match ( match (
tessellation_evaluation_stage.is_some(), stages_present.intersects(ShaderStages::FRAGMENT),
need_pre_rasterization_shader_state, need_fragment_shader_state,
) { ) {
(true, false) => {
return Err(Box::new(ValidationError {
problem: "the pipeline is not being created with \
pre-rasterization shader state, but `stages` contains a \
`ShaderStage::TessellationEvaluation` stage"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-06895"],
..Default::default()
}));
}
(false, true) => (),
_ => (),
}
if stages_present.intersects(ShaderStages::TESSELLATION_CONTROL)
&& !stages_present.intersects(ShaderStages::TESSELLATION_EVALUATION)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains a `ShaderStage::TessellationControl` stage, but not a \
`ShaderStage::TessellationEvaluation` stage"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-00729"],
..Default::default()
}));
} else if stages_present.intersects(ShaderStages::TESSELLATION_EVALUATION)
&& !stages_present.intersects(ShaderStages::TESSELLATION_CONTROL)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains a `ShaderStage::TessellationEvaluation` stage, but not a \
`ShaderStage::TessellationControl` stage"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-00730"],
..Default::default()
}));
}
match (
geometry_stage.is_some(),
need_pre_rasterization_shader_state,
) {
(true, false) => {
return Err(Box::new(ValidationError {
problem: "the pipeline is not being created with \
pre-rasterization shader state, but `stages` contains a \
`ShaderStage::Geometry` stage"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-06895"],
..Default::default()
}));
}
(false, true) => (),
_ => (),
}
match (fragment_stage.is_some(), need_fragment_shader_state) {
(true, false) => { (true, false) => {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
problem: "the pipeline is not being created with \ problem: "the pipeline is not being created with \
@ -1956,6 +1877,16 @@ impl GraphicsPipelineCreateInfo {
Validate shader stages individually Validate shader stages individually
*/ */
let mut has_mesh_ext = false;
let mut has_mesh_nv = false;
let mut vertex_stage = None;
let mut tessellation_control_stage = None;
let mut tessellation_evaluation_stage = None;
let mut geometry_stage = None;
let mut task_stage = None;
let mut mesh_stage = None;
let mut fragment_stage = None;
for (stage_index, stage) in stages.iter().enumerate() { for (stage_index, stage) in stages.iter().enumerate() {
stage stage
.validate(device) .validate(device)
@ -1969,6 +1900,37 @@ impl GraphicsPipelineCreateInfo {
} = stage; } = stage;
let entry_point_info = entry_point.info(); let entry_point_info = entry_point.info();
let execution_model = entry_point_info.execution_model;
match execution_model {
ExecutionModel::TaskEXT | ExecutionModel::MeshEXT => {
has_mesh_ext = true;
}
ExecutionModel::TaskNV | ExecutionModel::MeshNV => {
has_mesh_nv = true;
}
_ => (),
}
let stage_enum = ShaderStage::from(execution_model);
let stage_slot = match stage_enum {
ShaderStage::Vertex => &mut vertex_stage,
ShaderStage::TessellationControl => &mut tessellation_control_stage,
ShaderStage::TessellationEvaluation => &mut tessellation_evaluation_stage,
ShaderStage::Geometry => &mut geometry_stage,
ShaderStage::Task => &mut task_stage,
ShaderStage::Mesh => &mut mesh_stage,
ShaderStage::Fragment => &mut fragment_stage,
_ => {
return Err(Box::new(ValidationError {
context: format!("stages[{}]", stage_index).into(),
problem: "is not a pre-rasterization or fragment shader stage".into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-06896"],
..Default::default()
}));
}
};
*stage_slot = Some(stage);
layout layout
.ensure_compatible_with_shader( .ensure_compatible_with_shader(
@ -1987,11 +1949,64 @@ impl GraphicsPipelineCreateInfo {
})?; })?;
} }
let ordered_stages: SmallVec<[_; 5]> = [ if stages_present.intersects(PRIMITIVE_SHADING_STAGES)
&& stages_present.intersects(MESH_SHADING_STAGES)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains both primitive shading stages and mesh shading stages".into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-02095"],
..Default::default()
}));
}
if stages_present.intersects(ShaderStages::TESSELLATION_CONTROL)
&& !stages_present.intersects(ShaderStages::TESSELLATION_EVALUATION)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains a `ShaderStage::TessellationControl` stage, but not a \
`ShaderStage::TessellationEvaluation` stage"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-00729"],
..Default::default()
}));
} else if stages_present.intersects(ShaderStages::TESSELLATION_EVALUATION)
&& !stages_present.intersects(ShaderStages::TESSELLATION_CONTROL)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains a `ShaderStage::TessellationEvaluation` stage, but not a \
`ShaderStage::TessellationControl` stage"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-00730"],
..Default::default()
}));
}
if has_mesh_ext && has_mesh_nv {
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains mesh shader stages from both the EXT and the NV version".into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-TaskNV-07063"],
..Default::default()
}));
}
// VUID-VkGraphicsPipelineCreateInfo-layout-01688
// Checked at pipeline layout creation time.
/*
Check compatibility between shader interfaces
*/
let ordered_stages: SmallVec<[_; 7]> = [
vertex_stage, vertex_stage,
tessellation_control_stage, tessellation_control_stage,
tessellation_evaluation_stage, tessellation_evaluation_stage,
geometry_stage, geometry_stage,
task_stage,
mesh_stage,
fragment_stage, fragment_stage,
] ]
.into_iter() .into_iter()
@ -2044,9 +2059,6 @@ impl GraphicsPipelineCreateInfo {
})?; })?;
} }
// VUID-VkGraphicsPipelineCreateInfo-layout-01688
// Checked at pipeline layout creation time.
/* /*
Validate states individually Validate states individually
*/ */
@ -2061,9 +2073,6 @@ impl GraphicsPipelineCreateInfo {
input_assembly_state input_assembly_state
.validate(device) .validate(device)
.map_err(|err| err.add_context("input_assembly_state"))?; .map_err(|err| err.add_context("input_assembly_state"))?;
// TODO:
// VUID-VkGraphicsPipelineCreateInfo-topology-00737
} }
if let Some(tessellation_state) = tessellation_state { if let Some(tessellation_state) = tessellation_state {
@ -2076,7 +2085,133 @@ impl GraphicsPipelineCreateInfo {
viewport_state viewport_state
.validate(device) .validate(device)
.map_err(|err| err.add_context("viewport_state"))?; .map_err(|err| err.add_context("viewport_state"))?;
}
if let Some(rasterization_state) = rasterization_state {
rasterization_state
.validate(device)
.map_err(|err| err.add_context("rasterization_state"))?;
}
if let Some(multisample_state) = multisample_state {
multisample_state
.validate(device)
.map_err(|err| err.add_context("multisample_state"))?;
}
if let Some(depth_stencil_state) = depth_stencil_state {
depth_stencil_state
.validate(device)
.map_err(|err| err.add_context("depth_stencil_state"))?;
}
if let Some(color_blend_state) = color_blend_state {
color_blend_state
.validate(device)
.map_err(|err| err.add_context("color_blend_state"))?;
}
if let Some(subpass) = subpass {
match subpass {
PipelineSubpassType::BeginRenderPass(subpass) => {
// VUID-VkGraphicsPipelineCreateInfo-commonparent
assert_eq!(device, subpass.render_pass().device().as_ref());
}
PipelineSubpassType::BeginRendering(rendering_info) => {
if !device.enabled_features().dynamic_rendering {
return Err(Box::new(ValidationError {
context: "subpass".into(),
problem: "is `PipelineRenderPassType::BeginRendering`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"dynamic_rendering",
)])]),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06576"],
}));
}
rendering_info
.validate(device)
.map_err(|err| err.add_context("subpass"))?;
}
}
}
if let Some(discard_rectangle_state) = discard_rectangle_state {
if !device.enabled_extensions().ext_discard_rectangles {
return Err(Box::new(ValidationError {
context: "discard_rectangle_state".into(),
problem: "is `Some`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_discard_rectangles",
)])]),
..Default::default()
}));
}
discard_rectangle_state
.validate(device)
.map_err(|err| err.add_context("discard_rectangle_state"))?;
}
for dynamic_state in dynamic_state.iter().copied() {
dynamic_state.validate_device(device).map_err(|err| {
err.add_context("dynamic_state")
.set_vuids(&["VUID-VkPipelineDynamicStateCreateInfo-pDynamicStates-parameter"])
})?;
}
/*
Check dynamic states against other things
*/
if stages_present.intersects(ShaderStages::MESH) {
if dynamic_state.contains(&DynamicState::PrimitiveTopology) {
return Err(Box::new(ValidationError {
problem: "`stages` includes a mesh shader, but `dynamic_state` contains \
`DynamicState::PrimitiveTopology`"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07065"],
..Default::default()
}));
}
if dynamic_state.contains(&DynamicState::PrimitiveRestartEnable) {
return Err(Box::new(ValidationError {
problem: "`stages` includes a mesh shader, but `dynamic_state` contains \
`DynamicState::PrimitiveRestartEnable`"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07066"],
..Default::default()
}));
}
if dynamic_state.contains(&DynamicState::PatchControlPoints) {
return Err(Box::new(ValidationError {
problem: "`stages` includes a mesh shader, but `dynamic_state` contains \
`DynamicState::PatchControlPoints`"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07066"],
..Default::default()
}));
}
if dynamic_state.contains(&DynamicState::VertexInput) {
return Err(Box::new(ValidationError {
problem: "`stages` includes a mesh shader, but `dynamic_state` contains \
`DynamicState::VertexInput`"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07067"],
..Default::default()
}));
}
}
if let Some(_input_assembly_state) = input_assembly_state {
// TODO:
// VUID-VkGraphicsPipelineCreateInfo-topology-00737
}
if let Some(viewport_state) = viewport_state {
let ViewportState { let ViewportState {
ref viewports, ref viewports,
ref scissors, ref scissors,
@ -2152,10 +2287,6 @@ impl GraphicsPipelineCreateInfo {
} }
if let Some(rasterization_state) = rasterization_state { if let Some(rasterization_state) = rasterization_state {
rasterization_state
.validate(device)
.map_err(|err| err.add_context("rasterization_state"))?;
let &RasterizationState { let &RasterizationState {
depth_clamp_enable: _, depth_clamp_enable: _,
rasterizer_discard_enable: _, rasterizer_discard_enable: _,
@ -2232,20 +2363,12 @@ impl GraphicsPipelineCreateInfo {
// VUID-VkGraphicsPipelineCreateInfo-renderPass-06059 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06059
} }
if let Some(multisample_state) = multisample_state { if let Some(_multisample_state) = multisample_state {
multisample_state
.validate(device)
.map_err(|err| err.add_context("multisample_state"))?;
// TODO: // TODO:
// VUID-VkGraphicsPipelineCreateInfo-lineRasterizationMode-02766 // VUID-VkGraphicsPipelineCreateInfo-lineRasterizationMode-02766
} }
if let Some(depth_stencil_state) = depth_stencil_state { if let Some(depth_stencil_state) = depth_stencil_state {
depth_stencil_state
.validate(device)
.map_err(|err| err.add_context("depth_stencil_state"))?;
let &DepthStencilState { let &DepthStencilState {
flags: _, flags: _,
ref depth, ref depth,
@ -2287,137 +2410,6 @@ impl GraphicsPipelineCreateInfo {
// VUID-VkGraphicsPipelineCreateInfo-renderPass-06040 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06040
} }
if let Some(color_blend_state) = color_blend_state {
color_blend_state
.validate(device)
.map_err(|err| err.add_context("color_blend_state"))?;
}
if let Some(subpass) = subpass {
match subpass {
PipelineSubpassType::BeginRenderPass(subpass) => {
// VUID-VkGraphicsPipelineCreateInfo-commonparent
assert_eq!(device, subpass.render_pass().device().as_ref());
if subpass.subpass_desc().view_mask != 0 {
if stages_present.intersects(
ShaderStages::TESSELLATION_CONTROL
| ShaderStages::TESSELLATION_EVALUATION,
) && !device.enabled_features().multiview_tessellation_shader
{
return Err(Box::new(ValidationError {
problem: "`stages` contains tessellation shaders, and \
`subpass` has a non-zero `view_mask`"
.into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
Requires::Feature("multiview_tessellation_shader"),
])]),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-renderPass-06047"],
..Default::default()
}));
}
if stages_present.intersects(ShaderStages::GEOMETRY)
&& !device.enabled_features().multiview_geometry_shader
{
return Err(Box::new(ValidationError {
problem: "`stages` contains a geometry shader, and \
`subpass` has a non-zero `view_mask`"
.into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
Requires::Feature("multiview_geometry_shader"),
])]),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-renderPass-06048"],
..Default::default()
}));
}
}
}
PipelineSubpassType::BeginRendering(rendering_info) => {
if !device.enabled_features().dynamic_rendering {
return Err(Box::new(ValidationError {
context: "subpass".into(),
problem: "is `PipelineRenderPassType::BeginRendering`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"dynamic_rendering",
)])]),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06576"],
}));
}
rendering_info
.validate(device)
.map_err(|err| err.add_context("subpass"))?;
let &PipelineRenderingCreateInfo {
view_mask,
color_attachment_formats: _,
depth_attachment_format: _,
stencil_attachment_format: _,
_ne: _,
} = rendering_info;
if view_mask != 0 {
if stages_present.intersects(
ShaderStages::TESSELLATION_CONTROL
| ShaderStages::TESSELLATION_EVALUATION,
) && !device.enabled_features().multiview_tessellation_shader
{
return Err(Box::new(ValidationError {
problem: "`stages` contains tessellation shaders, and \
`subpass.view_mask` is not 0"
.into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
Requires::Feature("multiview_tessellation_shader"),
])]),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-renderPass-06057"],
..Default::default()
}));
}
if stages_present.intersects(ShaderStages::GEOMETRY)
&& !device.enabled_features().multiview_geometry_shader
{
return Err(Box::new(ValidationError {
problem: "`stages` contains a geometry shader, and \
`subpass.view_mask` is not 0"
.into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
Requires::Feature("multiview_geometry_shader"),
])]),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-renderPass-06058"],
..Default::default()
}));
}
}
}
}
}
if let Some(discard_rectangle_state) = discard_rectangle_state {
if !device.enabled_extensions().ext_discard_rectangles {
return Err(Box::new(ValidationError {
context: "discard_rectangle_state".into(),
problem: "is `Some`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_discard_rectangles",
)])]),
..Default::default()
}));
}
discard_rectangle_state
.validate(device)
.map_err(|err| err.add_context("discard_rectangle_state"))?;
}
for dynamic_state in dynamic_state.iter().copied() {
dynamic_state.validate_device(device).map_err(|err| {
err.add_context("dynamic_state")
.set_vuids(&["VUID-VkPipelineDynamicStateCreateInfo-pDynamicStates-parameter"])
})?;
}
/* /*
Checks that rely on multiple pieces of state Checks that rely on multiple pieces of state
*/ */
@ -2704,6 +2696,70 @@ impl GraphicsPipelineCreateInfo {
} }
} }
if let Some(subpass) = subpass {
let view_mask = match subpass {
PipelineSubpassType::BeginRenderPass(subpass) => subpass.subpass_desc().view_mask,
PipelineSubpassType::BeginRendering(rendering_info) => rendering_info.view_mask,
};
if view_mask != 0 {
if stages_present.intersects(
ShaderStages::TESSELLATION_CONTROL | ShaderStages::TESSELLATION_EVALUATION,
) && !device.enabled_features().multiview_tessellation_shader
{
return Err(Box::new(ValidationError {
problem: "`stages` contains tessellation shaders, and \
`subpass` has a non-zero `view_mask`"
.into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"multiview_tessellation_shader",
)])]),
vuids: &[
"VUID-VkGraphicsPipelineCreateInfo-renderPass-06047",
"VUID-VkGraphicsPipelineCreateInfo-renderPass-06057",
],
..Default::default()
}));
}
if stages_present.intersects(ShaderStages::GEOMETRY)
&& !device.enabled_features().multiview_geometry_shader
{
return Err(Box::new(ValidationError {
problem: "`stages` contains a geometry shader, and \
`subpass` has a non-zero `view_mask`"
.into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"multiview_geometry_shader",
)])]),
vuids: &[
"VUID-VkGraphicsPipelineCreateInfo-renderPass-06048",
"VUID-VkGraphicsPipelineCreateInfo-renderPass-06058",
],
..Default::default()
}));
}
if stages_present.intersects(ShaderStages::MESH)
&& !device.enabled_features().multiview_mesh_shader
{
return Err(Box::new(ValidationError {
problem: "`stages` contains a mesh shader, and \
`subpass` has a non-zero `view_mask`"
.into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"multiview_mesh_shader",
)])]),
vuids: &[
"VUID-VkGraphicsPipelineCreateInfo-renderPass-07064",
"VUID-VkGraphicsPipelineCreateInfo-renderPass-07720",
],
..Default::default()
}));
}
}
}
if let (Some(color_blend_state), Some(subpass)) = (color_blend_state, subpass) { if let (Some(color_blend_state), Some(subpass)) = (color_blend_state, subpass) {
let color_attachment_count = match subpass { let color_attachment_count = match subpass {
PipelineSubpassType::BeginRenderPass(subpass) => { PipelineSubpassType::BeginRenderPass(subpass) => {

View File

@ -74,8 +74,8 @@ impl PipelineShaderStageCreateInfo {
.set_vuids(&["VUID-VkPipelineShaderStageCreateInfo-flags-parameter"]) .set_vuids(&["VUID-VkPipelineShaderStageCreateInfo-flags-parameter"])
})?; })?;
let entry_point_info = entry_point.info(); let execution_model = entry_point.info().execution_model;
let stage_enum = ShaderStage::from(entry_point_info.execution_model); let stage_enum = ShaderStage::from(execution_model);
stage_enum.validate_device(device).map_err(|err| { stage_enum.validate_device(device).map_err(|err| {
err.add_context("entry_point.info().execution") err.add_context("entry_point.info().execution")
@ -144,18 +144,6 @@ impl PipelineShaderStageCreateInfo {
ShaderStage::Miss => (), ShaderStage::Miss => (),
ShaderStage::Intersection => (), ShaderStage::Intersection => (),
ShaderStage::Callable => (), 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 => { ShaderStage::Mesh => {
if !device.enabled_features().mesh_shader { if !device.enabled_features().mesh_shader {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
@ -168,6 +156,18 @@ impl PipelineShaderStageCreateInfo {
})); }));
} }
} }
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::SubpassShading => (), ShaderStage::SubpassShading => (),
} }

View File

@ -44,6 +44,17 @@ impl QueryPool {
unsafe { Ok(Self::new_unchecked(device, create_info)?) } unsafe { Ok(Self::new_unchecked(device, create_info)?) }
} }
fn validate_new(
device: &Device,
create_info: &QueryPoolCreateInfo,
) -> Result<(), Box<ValidationError>> {
create_info
.validate(device)
.map_err(|err| err.add_context("create_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked( pub unsafe fn new_unchecked(
device: Arc<Device>, device: Arc<Device>,
@ -86,17 +97,6 @@ impl QueryPool {
Ok(Self::from_handle(device, handle, create_info)) Ok(Self::from_handle(device, handle, create_info))
} }
fn validate_new(
device: &Device,
create_info: &QueryPoolCreateInfo,
) -> Result<(), Box<ValidationError>> {
create_info
.validate(device)
.map_err(|err| err.add_context("create_info"))?;
Ok(())
}
/// Creates a new `QueryPool` from a raw object handle. /// Creates a new `QueryPool` from a raw object handle.
/// ///
/// # Safety /// # Safety
@ -247,7 +247,8 @@ impl QueryPool {
| QueryType::AccelerationStructureCompactedSize | QueryType::AccelerationStructureCompactedSize
| QueryType::AccelerationStructureSerializationSize | QueryType::AccelerationStructureSerializationSize
| QueryType::AccelerationStructureSerializationBottomLevelPointers | QueryType::AccelerationStructureSerializationBottomLevelPointers
| QueryType::AccelerationStructureSize => (), | QueryType::AccelerationStructureSize
| QueryType::MeshPrimitivesGenerated => (),
} }
Ok(()) Ok(())
@ -373,6 +374,35 @@ impl QueryPoolCreateInfo {
err.add_context("query_type.flags") err.add_context("query_type.flags")
.set_vuids(&["VUID-VkQueryPoolCreateInfo-queryType-00792"]) .set_vuids(&["VUID-VkQueryPoolCreateInfo-queryType-00792"])
})?; })?;
if flags.intersects(
QueryPipelineStatisticFlags::TASK_SHADER_INVOCATIONS
| QueryPipelineStatisticFlags::MESH_SHADER_INVOCATIONS,
) && !device.enabled_features().mesh_shader_queries
{
return Err(Box::new(ValidationError {
context: "query_type.flags".into(),
problem: "contains `TASK_SHADER_INVOCATIONS` or \
`MESH_SHADER_INVOCATIONS`"
.into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"mesh_shader_queries",
)])]),
vuids: &["VUID-VkQueryPoolCreateInfo-meshShaderQueries-07069"],
}));
}
}
QueryType::MeshPrimitivesGenerated => {
if !device.enabled_features().mesh_shader_queries {
return Err(Box::new(ValidationError {
context: "query_type".into(),
problem: "is `QueryType::MeshPrimitivesGenerated`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"mesh_shader_queries",
)])]),
vuids: &["VUID-VkQueryPoolCreateInfo-meshShaderQueries-07068"],
}));
}
} }
QueryType::Occlusion QueryType::Occlusion
| QueryType::Timestamp | QueryType::Timestamp
@ -461,6 +491,14 @@ pub enum QueryType {
/// ///
/// [`write_acceleration_structures_properties`]: crate::command_buffer::RecordingCommandBuffer::write_acceleration_structures_properties /// [`write_acceleration_structures_properties`]: crate::command_buffer::RecordingCommandBuffer::write_acceleration_structures_properties
AccelerationStructureSize = ash::vk::QueryType::ACCELERATION_STRUCTURE_SIZE_KHR.as_raw(), AccelerationStructureSize = ash::vk::QueryType::ACCELERATION_STRUCTURE_SIZE_KHR.as_raw(),
/// Queries the number of primitives emitted from a mesh shader that reach the fragment shader.
///
/// Used with the [`begin_query`] and [`end_query`] commands.
///
/// [`begin_query`]: crate::command_buffer::RecordingCommandBuffer::begin_query
/// [`end_query`]: crate::command_buffer::RecordingCommandBuffer::end_query
MeshPrimitivesGenerated = ash::vk::QueryType::MESH_PRIMITIVES_GENERATED_EXT.as_raw(),
} }
impl QueryType { impl QueryType {
@ -485,7 +523,8 @@ impl QueryType {
| Self::AccelerationStructureCompactedSize | Self::AccelerationStructureCompactedSize
| Self::AccelerationStructureSerializationSize | Self::AccelerationStructureSerializationSize
| Self::AccelerationStructureSerializationBottomLevelPointers | Self::AccelerationStructureSerializationBottomLevelPointers
| Self::AccelerationStructureSize => 1, | Self::AccelerationStructureSize
| Self::MeshPrimitivesGenerated => 1,
Self::PipelineStatistics(flags) => flags.count() as DeviceSize, Self::PipelineStatistics(flags) => flags.count() as DeviceSize,
} }
} }
@ -541,6 +580,17 @@ impl QueryType {
})); }));
} }
} }
QueryType::MeshPrimitivesGenerated => {
if !device.enabled_extensions().ext_mesh_shader {
return Err(Box::new(ValidationError {
problem: "is `QueryType::MeshPrimitivesGenerated`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
Requires::DeviceExtension("ext_mesh_shader"),
])]),
..Default::default()
}));
}
}
} }
Ok(()) Ok(())
@ -566,6 +616,7 @@ impl From<&QueryType> for ash::vk::QueryType {
QueryType::AccelerationStructureSize => { QueryType::AccelerationStructureSize => {
ash::vk::QueryType::ACCELERATION_STRUCTURE_SIZE_KHR ash::vk::QueryType::ACCELERATION_STRUCTURE_SIZE_KHR
} }
QueryType::MeshPrimitivesGenerated => ash::vk::QueryType::MESH_PRIMITIVES_GENERATED_EXT,
} }
} }
} }
@ -596,6 +647,14 @@ vulkan_bitflags! {
/// Returns `true` if `self` contains any flags referring to graphics operations. /// Returns `true` if `self` contains any flags referring to graphics operations.
#[inline] #[inline]
pub const fn is_graphics(self) -> bool { pub const fn is_graphics(self) -> bool {
self.is_primitive_shading_graphics() || self.is_mesh_shading_graphics() ||
self.intersects(QueryPipelineStatisticFlags::FRAGMENT_SHADER_INVOCATIONS)
}
/// Returns `true` if `self` contains any flags referring to primitive shading graphics
/// operations.
#[inline]
pub const fn is_primitive_shading_graphics(self) -> bool {
self.intersects( self.intersects(
(QueryPipelineStatisticFlags::INPUT_ASSEMBLY_VERTICES) (QueryPipelineStatisticFlags::INPUT_ASSEMBLY_VERTICES)
.union(QueryPipelineStatisticFlags::INPUT_ASSEMBLY_PRIMITIVES) .union(QueryPipelineStatisticFlags::INPUT_ASSEMBLY_PRIMITIVES)
@ -604,11 +663,20 @@ vulkan_bitflags! {
.union(QueryPipelineStatisticFlags::GEOMETRY_SHADER_PRIMITIVES) .union(QueryPipelineStatisticFlags::GEOMETRY_SHADER_PRIMITIVES)
.union(QueryPipelineStatisticFlags::CLIPPING_INVOCATIONS) .union(QueryPipelineStatisticFlags::CLIPPING_INVOCATIONS)
.union(QueryPipelineStatisticFlags::CLIPPING_PRIMITIVES) .union(QueryPipelineStatisticFlags::CLIPPING_PRIMITIVES)
.union(QueryPipelineStatisticFlags::FRAGMENT_SHADER_INVOCATIONS)
.union(QueryPipelineStatisticFlags::TESSELLATION_CONTROL_SHADER_PATCHES) .union(QueryPipelineStatisticFlags::TESSELLATION_CONTROL_SHADER_PATCHES)
.union(QueryPipelineStatisticFlags::TESSELLATION_EVALUATION_SHADER_INVOCATIONS), .union(QueryPipelineStatisticFlags::TESSELLATION_EVALUATION_SHADER_INVOCATIONS),
) )
} }
/// Returns `true` if `self` contains any flags referring to mesh shading graphics
/// operations.
#[inline]
pub const fn is_mesh_shading_graphics(self) -> bool {
self.intersects(
(QueryPipelineStatisticFlags::TASK_SHADER_INVOCATIONS)
.union(QueryPipelineStatisticFlags::MESH_SHADER_INVOCATIONS),
)
}
} }
= QueryPipelineStatisticFlags(u32); = QueryPipelineStatisticFlags(u32);
@ -645,19 +713,17 @@ vulkan_bitflags! {
/// Count the number of times a compute shader is invoked. /// Count the number of times a compute shader is invoked.
COMPUTE_SHADER_INVOCATIONS = COMPUTE_SHADER_INVOCATIONS, COMPUTE_SHADER_INVOCATIONS = COMPUTE_SHADER_INVOCATIONS,
/* TODO: enable /// Count the number of times a task shader is invoked.
// TODO: document TASK_SHADER_INVOCATIONS = TASK_SHADER_INVOCATIONS_EXT
TASK_SHADER_INVOCATIONS = TASK_SHADER_INVOCATIONS_NV
RequiresOneOf([ RequiresOneOf([
RequiresAllOf([DeviceExtension(nv_mesh_shader)]), RequiresAllOf([DeviceExtension(ext_mesh_shader)]),
]),*/ ]),
/* TODO: enable /// Count the number of times a mesh shader is invoked.
// TODO: document MESH_SHADER_INVOCATIONS = MESH_SHADER_INVOCATIONS_EXT
MESH_SHADER_INVOCATIONS = MESH_SHADER_INVOCATIONS_NV
RequiresOneOf([ RequiresOneOf([
RequiresAllOf([DeviceExtension(nv_mesh_shader)]), RequiresAllOf([DeviceExtension(ext_mesh_shader)]),
]),*/ ]),
} }
/// A trait for elements of buffers that can be used as a destination for query results. /// A trait for elements of buffers that can be used as a destination for query results.