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

View File

@ -172,6 +172,31 @@ pub struct DrawIndirectCommand {
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
/// [`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
//! 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.
//! 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
//! into primitives (points, lines, triangles etc.).
//! 2. Vertex shader invocations: the vertex data of each primitive is fed as input to the vertex
@ -17,14 +33,31 @@
//! newly created vertices.
//! 4. (Optional) Geometry shading: whole primitives are fed as input and processed into a new set
//! 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.
//! - Perspective division.
//! - 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
//! 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
//! be performed both before and after the fragment shader ("early" and "late" fragment tests),
//! including:
@ -34,13 +67,11 @@
//! - Depth bounds test
//! - Stencil 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
//! in the framebuffer.
//!
//! A graphics pipeline contains many configuration options, which are grouped into collections of
//! "state". Often, these directly correspond to one or more steps in the graphics pipeline. Each
//! state collection has a dedicated submodule.
//! # Using a graphics pipeline
//!
//! 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
@ -117,10 +148,9 @@ pub struct GraphicsPipeline {
id: NonZeroU64,
flags: PipelineCreateFlags,
// TODO: replace () with an object that describes the shaders in some way.
shaders: HashMap<ShaderStage, ()>,
shader_stages: ShaderStages,
vertex_input_state: Option<VertexInputState>,
input_assembly_state: InputAssemblyState,
input_assembly_state: Option<InputAssemblyState>,
tessellation_state: Option<TessellationState>,
viewport_state: Option<ViewportState>,
rasterization_state: RasterizationState,
@ -137,6 +167,7 @@ pub struct GraphicsPipeline {
num_used_descriptor_sets: u32,
fixed_state: HashSet<DynamicState>,
fragment_tests_stages: Option<FragmentTestsStages>,
mesh_is_nv: bool,
// Note: this is only `Some` if `vertex_input_state` is `None`.
required_vertex_inputs: Option<HashMap<u32, ShaderInterfaceLocationInfo>>,
}
@ -917,7 +948,8 @@ impl GraphicsPipeline {
_ne: _,
} = 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<
(u32, u32),
DescriptorBindingRequirements,
@ -932,7 +964,7 @@ impl GraphicsPipeline {
let entry_point_info = entry_point.info();
let stage = ShaderStage::from(entry_point_info.execution_model);
shaders.insert(stage, ());
shader_stages |= stage.into();
let spirv = entry_point.module().spirv();
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 => {
fragment_tests_stages = Some(FragmentTestsStages::Late);
@ -1055,9 +1088,9 @@ impl GraphicsPipeline {
id: Self::next_id(),
flags,
shaders,
shader_stages,
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,
viewport_state,
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,
fixed_state,
fragment_tests_stages,
mesh_is_nv,
required_vertex_inputs,
})
}
@ -1090,16 +1124,15 @@ impl GraphicsPipeline {
self.flags
}
/// Returns information about a particular shader.
///
/// `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
/// Returns the shader stages that this pipeline contains.
#[inline]
pub(crate) fn shader(&self, stage: ShaderStage) -> Option<()> {
self.shaders.get(&stage).copied()
pub fn shader_stages(&self) -> ShaderStages {
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.
@ -1110,8 +1143,8 @@ impl GraphicsPipeline {
/// Returns the input assembly state used to create this pipeline.
#[inline]
pub fn input_assembly_state(&self) -> &InputAssemblyState {
&self.input_assembly_state
pub fn input_assembly_state(&self) -> Option<&InputAssemblyState> {
self.input_assembly_state.as_ref()
}
/// Returns the tessellation state used to create this pipeline.
@ -1251,35 +1284,39 @@ pub struct GraphicsPipelineCreateInfo {
/// 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.
pub stages: SmallVec<[PipelineShaderStageCreateInfo; 5]>,
/// 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`.
pub vertex_input_state: Option<VertexInputState>,
/// 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`.
pub input_assembly_state: Option<InputAssemblyState>,
/// 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`.
pub tessellation_state: Option<TessellationState>,
/// 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`.
///
@ -1288,14 +1325,15 @@ pub struct GraphicsPipelineCreateInfo {
/// The rasterization state.
///
/// This state is always used, and must be provided.
/// This must always be `Some`.
///
/// The default value is `None`.
pub rasterization_state: Option<RasterizationState>,
/// 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`.
///
@ -1304,8 +1342,9 @@ pub struct GraphicsPipelineCreateInfo {
/// 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.
/// It must be `None` otherwise.
///
/// The default value is `None`.
///
@ -1314,8 +1353,9 @@ pub struct GraphicsPipelineCreateInfo {
/// 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.
/// It must be `None` otherwise.
///
/// The default value is `None`.
///
@ -1350,8 +1390,6 @@ pub struct GraphicsPipelineCreateInfo {
/// The discard rectangle state.
///
/// This state is always used if it is provided.
///
/// The default value is `None`.
pub discard_rectangle_state: Option<DiscardRectangleState>,
@ -1448,17 +1486,17 @@ impl GraphicsPipelineCreateInfo {
Gather shader stages
*/
let mut stages_present = ShaderStages::empty();
let mut vertex_stage = None;
let mut tessellation_control_stage = None;
let mut tessellation_evaluation_stage = None;
let mut geometry_stage = None;
let mut fragment_stage = None;
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);
for (stage_index, stage) in stages.iter().enumerate() {
let entry_point_info = stage.entry_point.info();
let stage_enum = ShaderStage::from(entry_point_info.execution_model);
let stage_flag = ShaderStages::from(stage_enum);
let mut stages_present = ShaderStages::empty();
for stage in stages {
let stage_flag =
ShaderStages::from(ShaderStage::from(stage.entry_point.info().execution_model));
if stages_present.intersects(stage_flag) {
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;
}
@ -1548,13 +1549,8 @@ impl GraphicsPipelineCreateInfo {
_ => (),
}
let need_vertex_input_state = need_pre_rasterization_shader_state
&& stages.iter().any(|stage| {
matches!(
stage.entry_point.info().execution_model,
ExecutionModel::Vertex
)
});
let need_vertex_input_state =
need_pre_rasterization_shader_state && stages_present.intersects(ShaderStages::VERTEX);
let need_fragment_shader_state = need_pre_rasterization_shader_state
&& (!rasterization_state
.as_ref()
@ -1568,109 +1564,34 @@ impl GraphicsPipelineCreateInfo {
.rasterizer_discard_enable
|| dynamic_state.contains(&DynamicState::RasterizerDiscardEnable));
match (vertex_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::Vertex` stage"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-06895"],
..Default::default()
}));
}
(false, true) => {
if need_pre_rasterization_shader_state {
if !stages_present.intersects(ShaderStages::VERTEX | ShaderStages::MESH) {
return Err(Box::new(ValidationError {
problem: "the pipeline is being created with \
pre-rasterization shader state, but `stages` does not contain a \
`ShaderStage::Vertex` stage"
`ShaderStage::Vertex` or `ShaderStage::Mesh` stage"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-stage-02096"],
..Default::default()
}));
}
_ => (),
}
match (
tessellation_control_stage.is_some(),
need_pre_rasterization_shader_state,
) {
(true, false) => {
} else {
if stages_present.intersects(PRIMITIVE_SHADING_STAGES | MESH_SHADING_STAGES) {
return Err(Box::new(ValidationError {
problem: "the pipeline is not being created with \
pre-rasterization shader state, but `stages` contains a \
`ShaderStage::TessellationControl` stage"
pre-rasterization shader stage"
.into(),
vuids: &["VUID-VkGraphicsPipelineCreateInfo-pStages-06895"],
..Default::default()
}));
}
(false, true) => (),
_ => (),
}
match (
tessellation_evaluation_stage.is_some(),
need_pre_rasterization_shader_state,
stages_present.intersects(ShaderStages::FRAGMENT),
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) => {
return Err(Box::new(ValidationError {
problem: "the pipeline is not being created with \
@ -1956,6 +1877,16 @@ impl GraphicsPipelineCreateInfo {
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() {
stage
.validate(device)
@ -1969,6 +1900,37 @@ impl GraphicsPipelineCreateInfo {
} = stage;
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
.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,
tessellation_control_stage,
tessellation_evaluation_stage,
geometry_stage,
task_stage,
mesh_stage,
fragment_stage,
]
.into_iter()
@ -2044,9 +2059,6 @@ impl GraphicsPipelineCreateInfo {
})?;
}
// VUID-VkGraphicsPipelineCreateInfo-layout-01688
// Checked at pipeline layout creation time.
/*
Validate states individually
*/
@ -2061,9 +2073,6 @@ impl GraphicsPipelineCreateInfo {
input_assembly_state
.validate(device)
.map_err(|err| err.add_context("input_assembly_state"))?;
// TODO:
// VUID-VkGraphicsPipelineCreateInfo-topology-00737
}
if let Some(tessellation_state) = tessellation_state {
@ -2076,7 +2085,133 @@ impl GraphicsPipelineCreateInfo {
viewport_state
.validate(device)
.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 {
ref viewports,
ref scissors,
@ -2152,10 +2287,6 @@ impl GraphicsPipelineCreateInfo {
}
if let Some(rasterization_state) = rasterization_state {
rasterization_state
.validate(device)
.map_err(|err| err.add_context("rasterization_state"))?;
let &RasterizationState {
depth_clamp_enable: _,
rasterizer_discard_enable: _,
@ -2232,20 +2363,12 @@ impl GraphicsPipelineCreateInfo {
// VUID-VkGraphicsPipelineCreateInfo-renderPass-06059
}
if let Some(multisample_state) = multisample_state {
multisample_state
.validate(device)
.map_err(|err| err.add_context("multisample_state"))?;
if let Some(_multisample_state) = multisample_state {
// TODO:
// VUID-VkGraphicsPipelineCreateInfo-lineRasterizationMode-02766
}
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 {
flags: _,
ref depth,
@ -2287,137 +2410,6 @@ impl GraphicsPipelineCreateInfo {
// 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
*/
@ -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) {
let color_attachment_count = match subpass {
PipelineSubpassType::BeginRenderPass(subpass) => {

View File

@ -74,8 +74,8 @@ impl PipelineShaderStageCreateInfo {
.set_vuids(&["VUID-VkPipelineShaderStageCreateInfo-flags-parameter"])
})?;
let entry_point_info = entry_point.info();
let stage_enum = ShaderStage::from(entry_point_info.execution_model);
let execution_model = entry_point.info().execution_model;
let stage_enum = ShaderStage::from(execution_model);
stage_enum.validate_device(device).map_err(|err| {
err.add_context("entry_point.info().execution")
@ -144,18 +144,6 @@ impl PipelineShaderStageCreateInfo {
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 {
@ -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 => (),
}

View File

@ -44,6 +44,17 @@ impl QueryPool {
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))]
pub unsafe fn new_unchecked(
device: Arc<Device>,
@ -86,17 +97,6 @@ impl QueryPool {
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.
///
/// # Safety
@ -247,7 +247,8 @@ impl QueryPool {
| QueryType::AccelerationStructureCompactedSize
| QueryType::AccelerationStructureSerializationSize
| QueryType::AccelerationStructureSerializationBottomLevelPointers
| QueryType::AccelerationStructureSize => (),
| QueryType::AccelerationStructureSize
| QueryType::MeshPrimitivesGenerated => (),
}
Ok(())
@ -373,6 +374,35 @@ impl QueryPoolCreateInfo {
err.add_context("query_type.flags")
.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::Timestamp
@ -461,6 +491,14 @@ pub enum QueryType {
///
/// [`write_acceleration_structures_properties`]: crate::command_buffer::RecordingCommandBuffer::write_acceleration_structures_properties
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 {
@ -485,7 +523,8 @@ impl QueryType {
| Self::AccelerationStructureCompactedSize
| Self::AccelerationStructureSerializationSize
| Self::AccelerationStructureSerializationBottomLevelPointers
| Self::AccelerationStructureSize => 1,
| Self::AccelerationStructureSize
| Self::MeshPrimitivesGenerated => 1,
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(())
@ -566,6 +616,7 @@ impl From<&QueryType> for ash::vk::QueryType {
QueryType::AccelerationStructureSize => {
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.
#[inline]
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(
(QueryPipelineStatisticFlags::INPUT_ASSEMBLY_VERTICES)
.union(QueryPipelineStatisticFlags::INPUT_ASSEMBLY_PRIMITIVES)
@ -604,11 +663,20 @@ vulkan_bitflags! {
.union(QueryPipelineStatisticFlags::GEOMETRY_SHADER_PRIMITIVES)
.union(QueryPipelineStatisticFlags::CLIPPING_INVOCATIONS)
.union(QueryPipelineStatisticFlags::CLIPPING_PRIMITIVES)
.union(QueryPipelineStatisticFlags::FRAGMENT_SHADER_INVOCATIONS)
.union(QueryPipelineStatisticFlags::TESSELLATION_CONTROL_SHADER_PATCHES)
.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);
@ -645,19 +713,17 @@ vulkan_bitflags! {
/// Count the number of times a compute shader is invoked.
COMPUTE_SHADER_INVOCATIONS = COMPUTE_SHADER_INVOCATIONS,
/* TODO: enable
// TODO: document
TASK_SHADER_INVOCATIONS = TASK_SHADER_INVOCATIONS_NV
/// Count the number of times a task shader is invoked.
TASK_SHADER_INVOCATIONS = TASK_SHADER_INVOCATIONS_EXT
RequiresOneOf([
RequiresAllOf([DeviceExtension(nv_mesh_shader)]),
]),*/
RequiresAllOf([DeviceExtension(ext_mesh_shader)]),
]),
/* TODO: enable
// TODO: document
MESH_SHADER_INVOCATIONS = MESH_SHADER_INVOCATIONS_NV
/// Count the number of times a mesh shader is invoked.
MESH_SHADER_INVOCATIONS = MESH_SHADER_INVOCATIONS_EXT
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.