mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-21 14:24:18 +00:00
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:
parent
b2bbe34896
commit
655ca5e6c9
@ -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
@ -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
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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) => {
|
||||
|
@ -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 => (),
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user