From 464fc0d5496070d4a7d25882b3701d706aaeb374 Mon Sep 17 00:00:00 2001 From: Rua Date: Sun, 3 Oct 2021 08:17:05 +0200 Subject: [PATCH] Track state in the pipeline, invalidate dynamic state when binding pipeline with fixed state (#1722) * Track state in the pipeline, invalidate dynamic state when binding pipeline with fixed state * Rename `Compare` to `CompareOp`, rearrange builder a little * Nicer and more failsafe command buffer dynamic state checking --- vulkano/src/command_buffer/auto.rs | 62 ++- vulkano/src/command_buffer/synced/builder.rs | 21 + vulkano/src/command_buffer/synced/commands.rs | 11 + .../command_buffer/validity/dynamic_state.rs | 142 ++++--- vulkano/src/pipeline/depth_stencil.rs | 20 +- .../src/pipeline/graphics_pipeline/builder.rs | 401 ++++++++++++------ vulkano/src/pipeline/graphics_pipeline/mod.rs | 131 +++--- vulkano/src/pipeline/mod.rs | 2 + vulkano/src/sampler.rs | 8 +- 9 files changed, 541 insertions(+), 257 deletions(-) diff --git a/vulkano/src/command_buffer/auto.rs b/vulkano/src/command_buffer/auto.rs index 889a34836..359027b8e 100644 --- a/vulkano/src/command_buffer/auto.rs +++ b/vulkano/src/command_buffer/auto.rs @@ -58,6 +58,7 @@ use crate::pipeline::vertex::VertexBuffersCollection; use crate::pipeline::viewport::Scissor; use crate::pipeline::viewport::Viewport; use crate::pipeline::ComputePipeline; +use crate::pipeline::DynamicState; use crate::pipeline::GraphicsPipeline; use crate::pipeline::PipelineBindPoint; use crate::query::QueryControlFlags; @@ -1773,16 +1774,34 @@ impl AutoCommandBufferBuilder { self } + // Helper function for dynamic state setting. + fn has_fixed_state(&self, state: DynamicState) -> bool { + self.state() + .pipeline_graphics() + .map(|pipeline| { + matches!( + pipeline.dynamic_state(state), + Some(crate::pipeline::DynamicStateMode::Fixed) + ) + }) + .unwrap_or(false) + } + /// Sets the dynamic blend constants for future draw calls. /// /// # Panics /// /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the currently bound graphics pipeline already contains this state internally. pub fn set_blend_constants(&mut self, constants: [f32; 4]) -> &mut Self { assert!( self.queue_family().supports_graphics(), "the queue family of the command buffer must support graphics operations" ); + assert!( + !self.has_fixed_state(DynamicState::BlendConstants), + "the currently bound graphics pipeline must not contain this state internally" + ); unsafe { self.inner.set_blend_constants(constants); @@ -1796,6 +1815,7 @@ impl AutoCommandBufferBuilder { /// # Panics /// /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the currently bound graphics pipeline already contains this state internally. /// - If the /// [`ext_depth_range_unrestricted`](crate::device::DeviceExtensions::ext_depth_range_unrestricted) /// device extension is not enabled, panics if `min` or `max` is not between 0.0 and 1.0 inclusive. @@ -1804,6 +1824,10 @@ impl AutoCommandBufferBuilder { self.queue_family().supports_graphics(), "the queue family of the command buffer must support graphics operations" ); + assert!( + !self.has_fixed_state(DynamicState::DepthBounds), + "the currently bound graphics pipeline must not contain this state internally" + ); if !self .device() @@ -1828,6 +1852,7 @@ impl AutoCommandBufferBuilder { /// # Panics /// /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the currently bound graphics pipeline already contains this state internally. /// - If the [`wide_lines`](crate::device::Features::wide_lines) feature is not enabled, panics /// if `line_width` is not 1.0. pub fn set_line_width(&mut self, line_width: f32) -> &mut Self { @@ -1835,6 +1860,10 @@ impl AutoCommandBufferBuilder { self.queue_family().supports_graphics(), "the queue family of the command buffer must support graphics operations" ); + assert!( + !self.has_fixed_state(DynamicState::LineWidth), + "the currently bound graphics pipeline must not contain this state internally" + ); if !self.device().enabled_features().wide_lines { assert!( @@ -1855,6 +1884,7 @@ impl AutoCommandBufferBuilder { /// # Panics /// /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the currently bound graphics pipeline already contains this state internally. /// - Panics if the highest scissor slot being set is greater than the /// [`max_viewports`](crate::device::Properties::max_viewports) device property. /// - If the [`multi_viewport`](crate::device::Features::multi_viewport) feature is not enabled, @@ -1863,12 +1893,16 @@ impl AutoCommandBufferBuilder { where I: IntoIterator, { - let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect(); - assert!( self.queue_family().supports_graphics(), "the queue family of the command buffer must support graphics operations" ); + assert!( + !self.has_fixed_state(DynamicState::Scissor), + "the currently bound graphics pipeline must not contain this state internally" + ); + + let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect(); assert!( first_scissor + scissors.len() as u32 <= self.device().physical_device().properties().max_viewports, @@ -1904,6 +1938,7 @@ impl AutoCommandBufferBuilder { /// # Panics /// /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the currently bound graphics pipeline already contains this state internally. pub fn set_stencil_compare_mask( &mut self, faces: StencilFaces, @@ -1913,6 +1948,10 @@ impl AutoCommandBufferBuilder { self.queue_family().supports_graphics(), "the queue family of the command buffer must support graphics operations" ); + assert!( + !self.has_fixed_state(DynamicState::StencilCompareMask), + "the currently bound graphics pipeline must not contain this state internally" + ); unsafe { self.inner.set_stencil_compare_mask(faces, compare_mask); @@ -1926,11 +1965,16 @@ impl AutoCommandBufferBuilder { /// # Panics /// /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the currently bound graphics pipeline already contains this state internally. pub fn set_stencil_reference(&mut self, faces: StencilFaces, reference: u32) -> &mut Self { assert!( self.queue_family().supports_graphics(), "the queue family of the command buffer must support graphics operations" ); + assert!( + !self.has_fixed_state(DynamicState::StencilReference), + "the currently bound graphics pipeline must not contain this state internally" + ); unsafe { self.inner.set_stencil_reference(faces, reference); @@ -1944,11 +1988,16 @@ impl AutoCommandBufferBuilder { /// # Panics /// /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the currently bound graphics pipeline already contains this state internally. pub fn set_stencil_write_mask(&mut self, faces: StencilFaces, write_mask: u32) -> &mut Self { assert!( self.queue_family().supports_graphics(), "the queue family of the command buffer must support graphics operations" ); + assert!( + !self.has_fixed_state(DynamicState::StencilWriteMask), + "the currently bound graphics pipeline must not contain this state internally" + ); unsafe { self.inner.set_stencil_write_mask(faces, write_mask); @@ -1962,6 +2011,7 @@ impl AutoCommandBufferBuilder { /// # Panics /// /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the currently bound graphics pipeline already contains this state internally. /// - Panics if the highest viewport slot being set is greater than the /// [`max_viewports`](crate::device::Properties::max_viewports) device property. /// - If the [`multi_viewport`](crate::device::Features::multi_viewport) feature is not enabled, @@ -1970,12 +2020,16 @@ impl AutoCommandBufferBuilder { where I: IntoIterator, { - let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect(); - assert!( self.queue_family().supports_graphics(), "the queue family of the command buffer must support graphics operations" ); + assert!( + !self.has_fixed_state(DynamicState::Viewport), + "the currently bound graphics pipeline must not contain this state internally" + ); + + let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect(); assert!( first_viewport + viewports.len() as u32 <= self.device().physical_device().properties().max_viewports, diff --git a/vulkano/src/command_buffer/synced/builder.rs b/vulkano/src/command_buffer/synced/builder.rs index c84e43eee..33d2ae06e 100644 --- a/vulkano/src/command_buffer/synced/builder.rs +++ b/vulkano/src/command_buffer/synced/builder.rs @@ -32,6 +32,7 @@ use crate::pipeline::layout::PipelineLayout; use crate::pipeline::viewport::Scissor; use crate::pipeline::viewport::Viewport; use crate::pipeline::ComputePipeline; +use crate::pipeline::DynamicState; use crate::pipeline::GraphicsPipeline; use crate::pipeline::PipelineBindPoint; use crate::render_pass::FramebufferAbstract; @@ -732,6 +733,26 @@ struct CurrentState { viewport: FnvHashMap, } +impl CurrentState { + fn reset_dynamic_states(&mut self, states: impl IntoIterator) { + for state in states { + // TODO: If more dynamic states are added to CurrentState, add match arms here + match state { + DynamicState::BlendConstants => self.blend_constants = None, + DynamicState::DepthBias => self.depth_bias = None, + DynamicState::DepthBounds => self.depth_bounds = None, + DynamicState::LineWidth => self.line_width = None, + DynamicState::StencilCompareMask => self.stencil_compare_mask = Default::default(), + DynamicState::StencilReference => self.stencil_reference = Default::default(), + DynamicState::StencilWriteMask => self.stencil_write_mask = Default::default(), + DynamicState::Scissor => self.scissor.clear(), + DynamicState::Viewport => self.viewport.clear(), + _ => (), + } + } + } +} + #[derive(Debug)] struct DescriptorSetState { descriptor_sets: FnvHashMap>, diff --git a/vulkano/src/command_buffer/synced/commands.rs b/vulkano/src/command_buffer/synced/commands.rs index 45b4967bc..04959e44f 100644 --- a/vulkano/src/command_buffer/synced/commands.rs +++ b/vulkano/src/command_buffer/synced/commands.rs @@ -39,6 +39,7 @@ use crate::pipeline::vertex::VertexInput; use crate::pipeline::viewport::Scissor; use crate::pipeline::viewport::Viewport; use crate::pipeline::ComputePipeline; +use crate::pipeline::DynamicStateMode; use crate::pipeline::GraphicsPipeline; use crate::pipeline::PipelineBindPoint; use crate::query::QueryControlFlags; @@ -286,6 +287,16 @@ impl SyncCommandBufferBuilder { } } + self.current_state + .reset_dynamic_states(pipeline.dynamic_states().filter_map(|(state, mode)| { + // Reset any states that are fixed in the new pipeline. The pipeline bind command + // will overwrite these states. + if matches!(mode, DynamicStateMode::Fixed) { + Some(state) + } else { + None + } + })); self.append_command(Cmd { pipeline }, &[]).unwrap(); self.current_state.pipeline_graphics = self.commands.last().cloned(); } diff --git a/vulkano/src/command_buffer/validity/dynamic_state.rs b/vulkano/src/command_buffer/validity/dynamic_state.rs index df75a27af..8900349eb 100644 --- a/vulkano/src/command_buffer/validity/dynamic_state.rs +++ b/vulkano/src/command_buffer/validity/dynamic_state.rs @@ -8,6 +8,8 @@ // according to those terms. use crate::command_buffer::synced::CommandBufferState; +use crate::pipeline::DynamicState; +use crate::pipeline::DynamicStateMode; use crate::pipeline::GraphicsPipeline; use std::error; use std::fmt; @@ -19,61 +21,95 @@ pub(in super::super) fn check_dynamic_state_validity( ) -> Result<(), CheckDynamicStateValidityError> { let device = pipeline.device(); - if pipeline.has_dynamic_blend_constants() { - if current_state.blend_constants().is_none() { - return Err(CheckDynamicStateValidityError::BlendConstantsNotSet); + for state in pipeline.dynamic_states().filter_map(|(state, mode)| { + if matches!(mode, DynamicStateMode::Dynamic) { + Some(state) + } else { + None } - } - - if pipeline.has_dynamic_depth_bounds() { - if current_state.blend_constants().is_none() { - return Err(CheckDynamicStateValidityError::BlendConstantsNotSet); - } - } - - if pipeline.has_dynamic_line_width() { - if current_state.line_width().is_none() { - return Err(CheckDynamicStateValidityError::LineWidthNotSet); - } - } - - if pipeline.has_dynamic_scissor() { - for num in 0..pipeline.num_viewports() { - if current_state.scissor(num).is_none() { - return Err(CheckDynamicStateValidityError::ScissorNotSet { num }); + }) { + match state { + DynamicState::BlendConstants => { + if current_state.blend_constants().is_none() { + return Err(CheckDynamicStateValidityError::BlendConstantsNotSet); + } } - } - } - - if pipeline.has_dynamic_stencil_compare_mask() { - let state = current_state.stencil_compare_mask(); - - if state.front.is_none() || state.back.is_none() { - return Err(CheckDynamicStateValidityError::StencilCompareMaskNotSet); - } - } - - if pipeline.has_dynamic_stencil_reference() { - let state = current_state.stencil_reference(); - - if state.front.is_none() || state.back.is_none() { - return Err(CheckDynamicStateValidityError::StencilReferenceNotSet); - } - } - - if pipeline.has_dynamic_stencil_write_mask() { - let state = current_state.stencil_write_mask(); - - if state.front.is_none() || state.back.is_none() { - return Err(CheckDynamicStateValidityError::StencilWriteMaskNotSet); - } - } - - if pipeline.has_dynamic_viewport() { - for num in 0..pipeline.num_viewports() { - if current_state.viewport(num).is_none() { - return Err(CheckDynamicStateValidityError::ViewportNotSet { num }); + DynamicState::ColorWriteEnable => todo!(), + DynamicState::CullMode => todo!(), + DynamicState::DepthBias => todo!(), + DynamicState::DepthBiasEnable => todo!(), + DynamicState::DepthBounds => { + if current_state.depth_bounds().is_none() { + return Err(CheckDynamicStateValidityError::DepthBoundsNotSet); + } } + DynamicState::DepthBoundsTestEnable => todo!(), + DynamicState::DepthCompareOp => todo!(), + DynamicState::DepthTestEnable => todo!(), + DynamicState::DepthWriteEnable => todo!(), + DynamicState::DiscardRectangle => todo!(), + DynamicState::ExclusiveScissor => todo!(), + DynamicState::FragmentShadingRate => todo!(), + DynamicState::FrontFace => todo!(), + DynamicState::LineStipple => todo!(), + DynamicState::LineWidth => { + if current_state.line_width().is_none() { + return Err(CheckDynamicStateValidityError::LineWidthNotSet); + } + } + DynamicState::LogicOp => todo!(), + DynamicState::PatchControlPoints => todo!(), + DynamicState::PrimitiveRestartEnable => todo!(), + DynamicState::PrimitiveTopology => todo!(), + DynamicState::RasterizerDiscardEnable => todo!(), + DynamicState::RayTracingPipelineStackSize => unreachable!( + "RayTracingPipelineStackSize dynamic state should not occur on a graphics pipeline" + ), + DynamicState::SampleLocations => todo!(), + DynamicState::Scissor => { + for num in 0..pipeline.num_viewports() { + if current_state.scissor(num).is_none() { + return Err(CheckDynamicStateValidityError::ScissorNotSet { num }); + } + } + } + DynamicState::ScissorWithCount => todo!(), + DynamicState::StencilCompareMask => { + let state = current_state.stencil_compare_mask(); + + if state.front.is_none() || state.back.is_none() { + return Err(CheckDynamicStateValidityError::StencilCompareMaskNotSet); + } + } + DynamicState::StencilOp => todo!(), + DynamicState::StencilReference => { + let state = current_state.stencil_reference(); + + if state.front.is_none() || state.back.is_none() { + return Err(CheckDynamicStateValidityError::StencilReferenceNotSet); + } + } + DynamicState::StencilTestEnable => todo!(), + DynamicState::StencilWriteMask => { + let state = current_state.stencil_write_mask(); + + if state.front.is_none() || state.back.is_none() { + return Err(CheckDynamicStateValidityError::StencilWriteMaskNotSet); + } + } + DynamicState::VertexInput => todo!(), + DynamicState::VertexInputBindingStride => todo!(), + DynamicState::Viewport => { + for num in 0..pipeline.num_viewports() { + if current_state.viewport(num).is_none() { + return Err(CheckDynamicStateValidityError::ViewportNotSet { num }); + } + } + } + DynamicState::ViewportCoarseSampleOrder => todo!(), + DynamicState::ViewportShadingRatePalette => todo!(), + DynamicState::ViewportWithCount => todo!(), + DynamicState::ViewportWScaling => todo!(), } } @@ -138,5 +174,3 @@ impl fmt::Display for CheckDynamicStateValidityError { ) } } - -// TODO: tests diff --git a/vulkano/src/pipeline/depth_stencil.rs b/vulkano/src/pipeline/depth_stencil.rs index 84d1e03e0..4bab8f6e0 100644 --- a/vulkano/src/pipeline/depth_stencil.rs +++ b/vulkano/src/pipeline/depth_stencil.rs @@ -28,7 +28,7 @@ use std::u32; pub struct DepthStencil { /// Comparison to use between the depth value of each fragment and the depth value currently /// in the depth buffer. - pub depth_compare: Compare, + pub depth_compare: CompareOp, /// If `true`, then the value in the depth buffer will be updated when the depth test succeeds. pub depth_write: bool, @@ -51,7 +51,7 @@ impl DepthStencil { pub fn disabled() -> DepthStencil { DepthStencil { depth_write: false, - depth_compare: Compare::Always, + depth_compare: CompareOp::Always, depth_bounds_test: DepthBounds::Disabled, stencil_front: Default::default(), stencil_back: Default::default(), @@ -64,7 +64,7 @@ impl DepthStencil { pub fn simple_depth_test() -> DepthStencil { DepthStencil { depth_write: true, - depth_compare: Compare::Less, + depth_compare: CompareOp::Less, depth_bounds_test: DepthBounds::Disabled, stencil_front: Default::default(), stencil_back: Default::default(), @@ -84,7 +84,7 @@ impl Default for DepthStencil { pub struct Stencil { /// The comparison to perform between the existing stencil value in the stencil buffer, and /// the reference value (given by `reference`). - pub compare: Compare, + pub compare: CompareOp, /// The operation to perform when both the depth test and the stencil test passed. pub pass_op: StencilOp, @@ -133,10 +133,10 @@ impl Stencil { #[inline] pub fn always_keep(&self) -> bool { match self.compare { - Compare::Always => { + CompareOp::Always => { self.pass_op == StencilOp::Keep && self.depth_fail_op == StencilOp::Keep } - Compare::Never => self.fail_op == StencilOp::Keep, + CompareOp::Never => self.fail_op == StencilOp::Keep, _ => { self.pass_op == StencilOp::Keep && self.fail_op == StencilOp::Keep @@ -150,7 +150,7 @@ impl Default for Stencil { #[inline] fn default() -> Stencil { Stencil { - compare: Compare::Never, + compare: CompareOp::Never, pass_op: StencilOp::Keep, fail_op: StencilOp::Keep, depth_fail_op: StencilOp::Keep, @@ -236,7 +236,7 @@ impl DepthBounds { /// Used for both depth testing and stencil testing. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(i32)] -pub enum Compare { +pub enum CompareOp { /// The test never passes. Never = ash::vk::CompareOp::NEVER.as_raw(), /// The test passes if `value < reference_value`. @@ -255,9 +255,9 @@ pub enum Compare { Always = ash::vk::CompareOp::ALWAYS.as_raw(), } -impl From for ash::vk::CompareOp { +impl From for ash::vk::CompareOp { #[inline] - fn from(val: Compare) -> Self { + fn from(val: CompareOp) -> Self { Self::from_raw(val as i32) } } diff --git a/vulkano/src/pipeline/graphics_pipeline/builder.rs b/vulkano/src/pipeline/graphics_pipeline/builder.rs index 9a30b2816..72b50af36 100644 --- a/vulkano/src/pipeline/graphics_pipeline/builder.rs +++ b/vulkano/src/pipeline/graphics_pipeline/builder.rs @@ -12,47 +12,30 @@ #![allow(deprecated)] use crate::check_errors; -use crate::descriptor_set::layout::DescriptorSetDesc; -use crate::descriptor_set::layout::DescriptorSetLayout; +use crate::descriptor_set::layout::{DescriptorSetDesc, DescriptorSetLayout}; use crate::device::Device; use crate::image::SampleCount; -use crate::pipeline::blend::AttachmentBlend; -use crate::pipeline::blend::AttachmentsBlend; -use crate::pipeline::blend::Blend; -use crate::pipeline::blend::LogicOp; +use crate::pipeline::blend::{AttachmentBlend, AttachmentsBlend, Blend, LogicOp}; use crate::pipeline::cache::PipelineCache; -use crate::pipeline::depth_stencil::Compare; -use crate::pipeline::depth_stencil::DepthBounds; -use crate::pipeline::depth_stencil::DepthStencil; -use crate::pipeline::graphics_pipeline::GraphicsPipeline; -use crate::pipeline::graphics_pipeline::GraphicsPipelineCreationError; -use crate::pipeline::graphics_pipeline::Inner as GraphicsPipelineInner; +use crate::pipeline::depth_stencil::{CompareOp, DepthBounds, DepthStencil}; +use crate::pipeline::graphics_pipeline::{ + GraphicsPipeline, GraphicsPipelineCreationError, Inner as GraphicsPipelineInner, +}; use crate::pipeline::input_assembly::PrimitiveTopology; -use crate::pipeline::layout::PipelineLayout; -use crate::pipeline::layout::PipelineLayoutCreationError; -use crate::pipeline::layout::PipelineLayoutPcRange; -use crate::pipeline::raster::CullMode; -use crate::pipeline::raster::DepthBiasControl; -use crate::pipeline::raster::FrontFace; -use crate::pipeline::raster::PolygonMode; -use crate::pipeline::raster::Rasterization; -use crate::pipeline::shader::EntryPointAbstract; -use crate::pipeline::shader::GraphicsEntryPoint; -use crate::pipeline::shader::GraphicsShaderType; -use crate::pipeline::shader::SpecializationConstants; -use crate::pipeline::vertex::BuffersDefinition; -use crate::pipeline::vertex::Vertex; -use crate::pipeline::vertex::VertexDefinition; -use crate::pipeline::vertex::VertexInputRate; -use crate::pipeline::viewport::Scissor; -use crate::pipeline::viewport::Viewport; -use crate::pipeline::viewport::ViewportsState; +use crate::pipeline::layout::{PipelineLayout, PipelineLayoutCreationError, PipelineLayoutPcRange}; +use crate::pipeline::raster::{CullMode, DepthBiasControl, FrontFace, PolygonMode, Rasterization}; +use crate::pipeline::shader::{ + EntryPointAbstract, GraphicsEntryPoint, GraphicsShaderType, SpecializationConstants, +}; +use crate::pipeline::vertex::{BuffersDefinition, Vertex, VertexDefinition, VertexInputRate}; +use crate::pipeline::viewport::{Scissor, Viewport, ViewportsState}; +use crate::pipeline::{DynamicState, DynamicStateMode}; use crate::render_pass::Subpass; use crate::VulkanObject; +use fnv::FnvHashMap; use smallvec::SmallVec; use std::collections::hash_map::{Entry, HashMap}; -use std::mem; -use std::mem::MaybeUninit; +use std::mem::{self, MaybeUninit}; use std::ptr; use std::sync::Arc; use std::u32; @@ -60,27 +43,29 @@ use std::u32; /// Prototype for a `GraphicsPipeline`. // TODO: we can optimize this by filling directly the raw vk structs pub struct GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> { - vertex_definition: Vdef, vertex_shader: Option<(GraphicsEntryPoint<'vs>, Vss)>, + tessellation_shaders: Option>, + geometry_shader: Option<(GraphicsEntryPoint<'gs>, Gss)>, + fragment_shader: Option<(GraphicsEntryPoint<'fs>, Fss)>, + + vertex_definition: Vdef, input_assembly: ash::vk::PipelineInputAssemblyStateCreateInfo, // Note: the `input_assembly_topology` member is temporary in order to not lose information // about the number of patches per primitive. input_assembly_topology: PrimitiveTopology, - tessellation: Option>, - geometry_shader: Option<(GraphicsEntryPoint<'gs>, Gss)>, viewport: Option, raster: Rasterization, multisample: ash::vk::PipelineMultisampleStateCreateInfo, - fragment_shader: Option<(GraphicsEntryPoint<'fs>, Fss)>, depth_stencil: DepthStencil, blend: Blend, + subpass: Option, cache: Option>, } // Additional parameters if tessellation is used. #[derive(Clone, Debug)] -struct TessInfo<'tcs, 'tes, Tcss, Tess> { +struct TessellationShaders<'tcs, 'tes, Tcss, Tess> { tessellation_control_shader: (GraphicsEntryPoint<'tcs>, Tcss), tessellation_evaluation_shader: (GraphicsEntryPoint<'tes>, Tess), } @@ -103,21 +88,23 @@ impl /// Builds a new empty builder. pub(super) fn new() -> Self { GraphicsPipelineBuilder { - vertex_definition: BuffersDefinition::new(), vertex_shader: None, + tessellation_shaders: None, + geometry_shader: None, + fragment_shader: None, + + vertex_definition: BuffersDefinition::new(), input_assembly: ash::vk::PipelineInputAssemblyStateCreateInfo { topology: PrimitiveTopology::TriangleList.into(), ..Default::default() }, input_assembly_topology: PrimitiveTopology::TriangleList, - tessellation: None, - geometry_shader: None, viewport: None, raster: Default::default(), multisample: ash::vk::PipelineMultisampleStateCreateInfo::default(), - fragment_shader: None, depth_stencil: DepthStencil::disabled(), blend: Blend::pass_through(), + subpass: None, cache: None, } @@ -156,10 +143,10 @@ where let (descriptor_set_layout_descs, push_constant_ranges) = { let stages: SmallVec<[&GraphicsEntryPoint; 5]> = std::array::IntoIter::new([ self.vertex_shader.as_ref().map(|s| &s.0), - self.tessellation + self.tessellation_shaders .as_ref() .map(|s| &s.tessellation_control_shader.0), - self.tessellation + self.tessellation_shaders .as_ref() .map(|s| &s.tessellation_evaluation_shader.0), self.geometry_shader.as_ref().map(|s| &s.0), @@ -254,7 +241,7 @@ where )?; } - if let Some(ref tess) = self.tessellation { + if let Some(ref tess) = self.tessellation_shaders { { let shader = &tess.tessellation_control_shader.0; pipeline_layout.ensure_compatible_with_shader( @@ -292,7 +279,8 @@ where } // Will contain the list of dynamic states. Filled throughout this function. - let mut dynamic_states: SmallVec<[ash::vk::DynamicState; 8]> = SmallVec::new(); + let mut dynamic_state_modes: FnvHashMap = + HashMap::default(); // Creating the specialization constants of the various stages. let vertex_shader_specialization = { @@ -311,7 +299,7 @@ where } }; - let tess_shader_specialization = if let Some(ref tess) = self.tessellation { + let tess_shader_specialization = if let Some(ref tess) = self.tessellation_shaders { let tcs_spec = { let shader = &tess.tessellation_control_shader; let spec_descriptors = Tcss::descriptors(); @@ -405,7 +393,7 @@ where ..Default::default() }); - if let Some(ref tess) = self.tessellation { + if let Some(ref tess) = self.tessellation_shaders { // FIXME: must check that the control shader and evaluation shader are compatible if !device.enabled_features().tessellation_shader { @@ -668,7 +656,7 @@ where None }; - let vertex_input_state = ash::vk::PipelineVertexInputStateCreateInfo { + let vertex_input_state = Some(ash::vk::PipelineVertexInputStateCreateInfo { p_next: if let Some(next) = vertex_input_divisor_state.as_ref() { next as *const _ as *const _ } else { @@ -680,7 +668,9 @@ where vertex_attribute_description_count: attribute_descriptions.len() as u32, p_vertex_attribute_descriptions: attribute_descriptions.as_ptr(), ..Default::default() - }; + }); + + let input_assembly_state = Some(self.input_assembly); if self.input_assembly.primitive_restart_enable != ash::vk::FALSE && !self.input_assembly_topology.supports_primitive_restart() @@ -706,9 +696,9 @@ where } } - let tessellation = match self.input_assembly_topology { + let tessellation_state = match self.input_assembly_topology { PrimitiveTopology::PatchList { vertices_per_patch } => { - if self.tessellation.is_none() { + if self.tessellation_shaders.is_none() { return Err(GraphicsPipelineCreationError::InvalidPrimitiveTopology); } if vertices_per_patch @@ -727,7 +717,7 @@ where }) } _ => { - if self.tessellation.is_some() { + if self.tessellation_shaders.is_some() { return Err(GraphicsPipelineCreationError::InvalidPrimitiveTopology); } @@ -751,7 +741,7 @@ where .iter() .map(|e| e.clone().into()) .collect::>(); - dynamic_states.push(ash::vk::DynamicState::VIEWPORT); + dynamic_state_modes.insert(DynamicState::Viewport, DynamicStateMode::Dynamic); (SmallVec::new(), scissors, num) } ViewportsState::DynamicScissors { ref viewports } => { @@ -760,12 +750,12 @@ where .iter() .map(|e| e.clone().into()) .collect::>(); - dynamic_states.push(ash::vk::DynamicState::SCISSOR); + dynamic_state_modes.insert(DynamicState::Scissor, DynamicStateMode::Dynamic); (viewports, SmallVec::new(), num) } ViewportsState::Dynamic { num } => { - dynamic_states.push(ash::vk::DynamicState::VIEWPORT); - dynamic_states.push(ash::vk::DynamicState::SCISSOR); + dynamic_state_modes.insert(DynamicState::Viewport, DynamicStateMode::Dynamic); + dynamic_state_modes.insert(DynamicState::Scissor, DynamicStateMode::Dynamic); (SmallVec::new(), SmallVec::new(), num) } }; @@ -805,7 +795,7 @@ where } } - let viewport_info = ash::vk::PipelineViewportStateCreateInfo { + let viewport_state = Some(ash::vk::PipelineViewportStateCreateInfo { flags: ash::vk::PipelineViewportStateCreateFlags::empty(), viewport_count: vp_num, p_viewports: if vp_vp.is_empty() { @@ -820,19 +810,19 @@ where vp_sc.as_ptr() }, // validation layer crashes if you just pass the pointer ..Default::default() - }; + }); if let Some(line_width) = self.raster.line_width { if line_width != 1.0 && !device.enabled_features().wide_lines { return Err(GraphicsPipelineCreationError::WideLinesFeatureNotEnabled); } } else { - dynamic_states.push(ash::vk::DynamicState::LINE_WIDTH); + dynamic_state_modes.insert(DynamicState::LineWidth, DynamicStateMode::Dynamic); } let (db_enable, db_const, db_clamp, db_slope) = match self.raster.depth_bias { DepthBiasControl::Dynamic => { - dynamic_states.push(ash::vk::DynamicState::DEPTH_BIAS); + dynamic_state_modes.insert(DynamicState::DepthBias, DynamicStateMode::Dynamic); (ash::vk::TRUE, 0.0, 0.0, 0.0) } DepthBiasControl::Disabled => (ash::vk::FALSE, 0.0, 0.0, 0.0), @@ -860,7 +850,7 @@ where return Err(GraphicsPipelineCreationError::FillModeNonSolidFeatureNotEnabled); } - let rasterization = ash::vk::PipelineRasterizationStateCreateInfo { + let rasterization_state = Some(ash::vk::PipelineRasterizationStateCreateInfo { flags: ash::vk::PipelineRasterizationStateCreateFlags::empty(), depth_clamp_enable: if self.raster.depth_clamp { ash::vk::TRUE @@ -881,7 +871,7 @@ where depth_bias_slope_factor: db_slope, line_width: self.raster.line_width.unwrap_or(1.0), ..Default::default() - }; + }); self.multisample.rasterization_samples = self .subpass @@ -905,7 +895,9 @@ where } } - let depth_stencil = { + let multisample_state = Some(self.multisample); + + let depth_stencil_state = { let db = match self.depth_stencil.depth_bounds_test { DepthBounds::Disabled => (ash::vk::FALSE, 0.0, 0.0), DepthBounds::Fixed(ref range) => { @@ -920,7 +912,8 @@ where return Err(GraphicsPipelineCreationError::DepthBoundsFeatureNotEnabled); } - dynamic_states.push(ash::vk::DynamicState::DEPTH_BOUNDS); + dynamic_state_modes + .insert(DynamicState::DepthBounds, DynamicStateMode::Dynamic); (ash::vk::TRUE, 0.0, 1.0) } @@ -932,7 +925,8 @@ where ) { (Some(_), Some(_)) => (), (None, None) => { - dynamic_states.push(ash::vk::DynamicState::STENCIL_COMPARE_MASK); + dynamic_state_modes + .insert(DynamicState::StencilCompareMask, DynamicStateMode::Dynamic); } _ => return Err(GraphicsPipelineCreationError::WrongStencilState), }; @@ -943,7 +937,8 @@ where ) { (Some(_), Some(_)) => (), (None, None) => { - dynamic_states.push(ash::vk::DynamicState::STENCIL_WRITE_MASK); + dynamic_state_modes + .insert(DynamicState::StencilWriteMask, DynamicStateMode::Dynamic); } _ => return Err(GraphicsPipelineCreationError::WrongStencilState), }; @@ -954,7 +949,8 @@ where ) { (Some(_), Some(_)) => (), (None, None) => { - dynamic_states.push(ash::vk::DynamicState::STENCIL_REFERENCE); + dynamic_state_modes + .insert(DynamicState::StencilReference, DynamicStateMode::Dynamic); } _ => return Err(GraphicsPipelineCreationError::WrongStencilState), }; @@ -965,7 +961,7 @@ where return Err(GraphicsPipelineCreationError::NoDepthAttachment); } - if self.depth_stencil.depth_compare != Compare::Always + if self.depth_stencil.depth_compare != CompareOp::Always && !self.subpass.as_ref().unwrap().has_depth() { return Err(GraphicsPipelineCreationError::NoDepthAttachment); @@ -980,10 +976,10 @@ where // FIXME: stencil writability - ash::vk::PipelineDepthStencilStateCreateInfo { + Some(ash::vk::PipelineDepthStencilStateCreateInfo { flags: ash::vk::PipelineDepthStencilStateCreateFlags::empty(), depth_test_enable: if !self.depth_stencil.depth_write - && self.depth_stencil.depth_compare == Compare::Always + && self.depth_stencil.depth_compare == CompareOp::Always { ash::vk::FALSE } else { @@ -1040,7 +1036,7 @@ where min_depth_bounds: db.1, max_depth_bounds: db.2, ..Default::default() - } + }) }; let blend_atch: SmallVec<[ash::vk::PipelineColorBlendAttachmentState; 8]> = { @@ -1068,7 +1064,7 @@ where } }; - let blend = ash::vk::PipelineColorBlendStateCreateInfo { + let color_blend_state = Some(ash::vk::PipelineColorBlendStateCreateInfo { flags: ash::vk::PipelineColorBlendStateCreateFlags::empty(), logic_op_enable: if self.blend.logic_op.is_some() { if !device.enabled_features().logic_op { @@ -1084,23 +1080,143 @@ where blend_constants: if let Some(c) = self.blend.blend_constants { c } else { - dynamic_states.push(ash::vk::DynamicState::BLEND_CONSTANTS); + dynamic_state_modes.insert(DynamicState::BlendConstants, DynamicStateMode::Dynamic); [0.0, 0.0, 0.0, 0.0] }, ..Default::default() - }; + }); - let dynamic_states = if !dynamic_states.is_empty() { + // Dynamic state + let dynamic_state_list: Vec = dynamic_state_modes + .iter() + .filter_map(|(&state, &mode)| { + if matches!(mode, DynamicStateMode::Dynamic) { + Some(state.into()) + } else { + None + } + }) + .collect(); + + let dynamic_state = if !dynamic_state_list.is_empty() { Some(ash::vk::PipelineDynamicStateCreateInfo { flags: ash::vk::PipelineDynamicStateCreateFlags::empty(), - dynamic_state_count: dynamic_states.len() as u32, - p_dynamic_states: dynamic_states.as_ptr(), + dynamic_state_count: dynamic_state_list.len() as u32, + p_dynamic_states: dynamic_state_list.as_ptr(), ..Default::default() }) } else { None }; + // Set any remaining states to fixed, if the corresponding state is enabled. + if vertex_input_state.is_some() { + dynamic_state_modes + .entry(DynamicState::VertexInput) + .or_insert(DynamicStateMode::Fixed); + } + + if input_assembly_state.is_some() { + dynamic_state_modes + .entry(DynamicState::PrimitiveTopology) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::PrimitiveRestartEnable) + .or_insert(DynamicStateMode::Fixed); + } + + if tessellation_state.is_some() { + dynamic_state_modes + .entry(DynamicState::PatchControlPoints) + .or_insert(DynamicStateMode::Fixed); + } + + if viewport_state.is_some() { + dynamic_state_modes + .entry(DynamicState::Viewport) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::Scissor) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::ViewportWithCount) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::ScissorWithCount) + .or_insert(DynamicStateMode::Fixed); + } + + if rasterization_state.is_some() { + dynamic_state_modes + .entry(DynamicState::RasterizerDiscardEnable) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::CullMode) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::FrontFace) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::DepthBiasEnable) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::DepthBias) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::LineWidth) + .or_insert(DynamicStateMode::Fixed); + } + + if depth_stencil_state.is_some() { + dynamic_state_modes + .entry(DynamicState::DepthTestEnable) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::DepthWriteEnable) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::DepthCompareOp) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::DepthBoundsTestEnable) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::StencilTestEnable) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::StencilCompareMask) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::StencilWriteMask) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::StencilReference) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::DepthBounds) + .or_insert(DynamicStateMode::Fixed); + } + + if color_blend_state.is_some() { + dynamic_state_modes + .entry(DynamicState::LogicOp) + .or_insert(DynamicStateMode::Fixed); + dynamic_state_modes + .entry(DynamicState::BlendConstants) + .or_insert(DynamicStateMode::Fixed); + } + + // Dynamic states not handled yet: + // - ViewportWScaling (VkPipelineViewportWScalingStateCreateInfoNV) + // - DiscardRectangle (VkPipelineDiscardRectangleStateCreateInfoEXT) + // - SampleLocations (VkPipelineSampleLocationsStateCreateInfoEXT) + // - ViewportShadingRatePalette (VkPipelineViewportShadingRateImageStateCreateInfoNV) + // - ViewportCoarseSampleOrder (VkPipelineViewportCoarseSampleOrderStateCreateInfoNV) + // - ExclusiveScissor (VkPipelineViewportExclusiveScissorStateCreateInfoNV) + // - FragmentShadingRate (VkPipelineFragmentShadingRateStateCreateInfoKHR) + // - LineStipple (VkPipelineRasterizationLineStateCreateInfoEXT) + // - ColorWriteEnable (VkPipelineColorWriteCreateInfoEXT) + if let Some(multiview) = self .subpass .as_ref() @@ -1120,7 +1236,7 @@ where return Err(GraphicsPipelineCreationError::MultiviewGeometryShaderNotSupported); } - if self.tessellation.is_some() + if self.tessellation_shaders.is_some() && !device .physical_device() .supported_features() @@ -1138,18 +1254,39 @@ where flags: ash::vk::PipelineCreateFlags::empty(), // TODO: some flags are available but none are critical stage_count: stages.len() as u32, p_stages: stages.as_ptr(), - p_vertex_input_state: &vertex_input_state, - p_input_assembly_state: &self.input_assembly, - p_tessellation_state: tessellation + p_vertex_input_state: vertex_input_state .as_ref() - .map(|t| t as *const _) + .map(|p| p as *const _) .unwrap_or(ptr::null()), - p_viewport_state: &viewport_info, - p_rasterization_state: &rasterization, - p_multisample_state: &self.multisample, - p_depth_stencil_state: &depth_stencil, - p_color_blend_state: &blend, - p_dynamic_state: dynamic_states + p_input_assembly_state: input_assembly_state + .as_ref() + .map(|p| p as *const _) + .unwrap_or(ptr::null()), + p_tessellation_state: tessellation_state + .as_ref() + .map(|p| p as *const _) + .unwrap_or(ptr::null()), + p_viewport_state: viewport_state + .as_ref() + .map(|p| p as *const _) + .unwrap_or(ptr::null()), + p_rasterization_state: rasterization_state + .as_ref() + .map(|p| p as *const _) + .unwrap_or(ptr::null()), + p_multisample_state: multisample_state + .as_ref() + .map(|p| p as *const _) + .unwrap_or(ptr::null()), + p_depth_stencil_state: depth_stencil_state + .as_ref() + .map(|p| p as *const _) + .unwrap_or(ptr::null()), + p_color_blend_state: color_blend_state + .as_ref() + .map(|p| p as *const _) + .unwrap_or(ptr::null()), + p_dynamic_state: dynamic_state .as_ref() .map(|s| s as *const _) .unwrap_or(ptr::null()), @@ -1199,17 +1336,7 @@ where layout: pipeline_layout, subpass: self.subpass.take().unwrap(), vertex_input, - - dynamic_line_width: self.raster.line_width.is_none(), - dynamic_viewport: self.viewport.as_ref().unwrap().dynamic_viewports(), - dynamic_scissor: self.viewport.as_ref().unwrap().dynamic_scissors(), - dynamic_depth_bias: self.raster.depth_bias.is_dynamic(), - dynamic_depth_bounds: self.depth_stencil.depth_bounds_test.is_dynamic(), - dynamic_stencil_compare_mask: self.depth_stencil.stencil_back.compare_mask.is_none(), - dynamic_stencil_write_mask: self.depth_stencil.stencil_back.write_mask.is_none(), - dynamic_stencil_reference: self.depth_stencil.stencil_back.reference.is_none(), - dynamic_blend_constants: self.blend.blend_constants.is_none(), - + dynamic_state: dynamic_state_modes, num_viewports: self.viewport.as_ref().unwrap().num_viewports(), }) } @@ -1229,18 +1356,20 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> vertex_definition: T, ) -> GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, T, Vss, Tcss, Tess, Gss, Fss> { GraphicsPipelineBuilder { - vertex_definition, vertex_shader: self.vertex_shader, + tessellation_shaders: self.tessellation_shaders, + geometry_shader: self.geometry_shader, + fragment_shader: self.fragment_shader, + + vertex_definition, input_assembly: self.input_assembly, input_assembly_topology: self.input_assembly_topology, - tessellation: self.tessellation, - geometry_shader: self.geometry_shader, viewport: self.viewport, raster: self.raster, multisample: self.multisample, - fragment_shader: self.fragment_shader, depth_stencil: self.depth_stencil, blend: self.blend, + subpass: self.subpass, cache: self.cache, } @@ -1281,18 +1410,20 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> Vss2: SpecializationConstants, { GraphicsPipelineBuilder { - vertex_definition: self.vertex_definition, vertex_shader: Some((shader, specialization_constants)), + tessellation_shaders: self.tessellation_shaders, + geometry_shader: self.geometry_shader, + fragment_shader: self.fragment_shader, + + vertex_definition: self.vertex_definition, input_assembly: self.input_assembly, input_assembly_topology: self.input_assembly_topology, - tessellation: self.tessellation, - geometry_shader: self.geometry_shader, viewport: self.viewport, raster: self.raster, multisample: self.multisample, - fragment_shader: self.fragment_shader, depth_stencil: self.depth_stencil, blend: self.blend, + subpass: self.subpass, cache: self.cache, } @@ -1433,11 +1564,8 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> Tess2: SpecializationConstants, { GraphicsPipelineBuilder { - vertex_definition: self.vertex_definition, vertex_shader: self.vertex_shader, - input_assembly: self.input_assembly, - input_assembly_topology: self.input_assembly_topology, - tessellation: Some(TessInfo { + tessellation_shaders: Some(TessellationShaders { tessellation_control_shader: ( tessellation_control_shader, tessellation_control_shader_spec_constants, @@ -1448,12 +1576,17 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> ), }), geometry_shader: self.geometry_shader, + fragment_shader: self.fragment_shader, + + vertex_definition: self.vertex_definition, + input_assembly: self.input_assembly, + input_assembly_topology: self.input_assembly_topology, viewport: self.viewport, raster: self.raster, multisample: self.multisample, - fragment_shader: self.fragment_shader, depth_stencil: self.depth_stencil, blend: self.blend, + subpass: self.subpass, cache: self.cache, } @@ -1462,7 +1595,7 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> /// Sets the tessellation shaders stage as disabled. This is the default. #[inline] pub fn tessellation_shaders_disabled(mut self) -> Self { - self.tessellation = None; + self.tessellation_shaders = None; self } @@ -1478,18 +1611,20 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> Gss2: SpecializationConstants, { GraphicsPipelineBuilder { - vertex_definition: self.vertex_definition, vertex_shader: self.vertex_shader, + tessellation_shaders: self.tessellation_shaders, + geometry_shader: Some((shader, specialization_constants)), + fragment_shader: self.fragment_shader, + + vertex_definition: self.vertex_definition, input_assembly: self.input_assembly, input_assembly_topology: self.input_assembly_topology, - tessellation: self.tessellation, - geometry_shader: Some((shader, specialization_constants)), viewport: self.viewport, raster: self.raster, multisample: self.multisample, - fragment_shader: self.fragment_shader, depth_stencil: self.depth_stencil, blend: self.blend, + subpass: self.subpass, cache: self.cache, } @@ -1760,18 +1895,20 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> Fss2: SpecializationConstants, { GraphicsPipelineBuilder { - vertex_definition: self.vertex_definition, vertex_shader: self.vertex_shader, + tessellation_shaders: self.tessellation_shaders, + geometry_shader: self.geometry_shader, + fragment_shader: Some((shader, specialization_constants)), + + vertex_definition: self.vertex_definition, input_assembly: self.input_assembly, input_assembly_topology: self.input_assembly_topology, - tessellation: self.tessellation, - geometry_shader: self.geometry_shader, viewport: self.viewport, raster: self.raster, multisample: self.multisample, - fragment_shader: Some((shader, specialization_constants)), depth_stencil: self.depth_stencil, blend: self.blend, + subpass: self.subpass, cache: self.cache, } @@ -1875,18 +2012,20 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> #[inline] pub fn render_pass(self, subpass: Subpass) -> Self { GraphicsPipelineBuilder { - vertex_definition: self.vertex_definition, vertex_shader: self.vertex_shader, + tessellation_shaders: self.tessellation_shaders, + geometry_shader: self.geometry_shader, + fragment_shader: self.fragment_shader, + + vertex_definition: self.vertex_definition, input_assembly: self.input_assembly, input_assembly_topology: self.input_assembly_topology, - tessellation: self.tessellation, - geometry_shader: self.geometry_shader, viewport: self.viewport, raster: self.raster, multisample: self.multisample, - fragment_shader: self.fragment_shader, depth_stencil: self.depth_stencil, blend: self.blend, + subpass: Some(subpass), cache: self.cache, } @@ -1916,18 +2055,20 @@ where { fn clone(&self) -> Self { GraphicsPipelineBuilder { - vertex_definition: self.vertex_definition.clone(), vertex_shader: self.vertex_shader.clone(), + tessellation_shaders: self.tessellation_shaders.clone(), + geometry_shader: self.geometry_shader.clone(), + fragment_shader: self.fragment_shader.clone(), + + vertex_definition: self.vertex_definition.clone(), input_assembly: unsafe { ptr::read(&self.input_assembly) }, input_assembly_topology: self.input_assembly_topology, - tessellation: self.tessellation.clone(), - geometry_shader: self.geometry_shader.clone(), viewport: self.viewport.clone(), raster: self.raster.clone(), multisample: self.multisample, - fragment_shader: self.fragment_shader.clone(), depth_stencil: self.depth_stencil.clone(), blend: self.blend.clone(), + subpass: self.subpass.clone(), cache: self.cache.clone(), } diff --git a/vulkano/src/pipeline/graphics_pipeline/mod.rs b/vulkano/src/pipeline/graphics_pipeline/mod.rs index 9f927ae0f..613080d18 100644 --- a/vulkano/src/pipeline/graphics_pipeline/mod.rs +++ b/vulkano/src/pipeline/graphics_pipeline/mod.rs @@ -16,6 +16,7 @@ use crate::pipeline::vertex::BuffersDefinition; use crate::pipeline::vertex::VertexInput; use crate::render_pass::Subpass; use crate::VulkanObject; +use fnv::FnvHashMap; use std::fmt; use std::hash::Hash; use std::hash::Hasher; @@ -38,17 +39,7 @@ pub struct GraphicsPipeline { layout: Arc, subpass: Subpass, vertex_input: VertexInput, - - dynamic_line_width: bool, - dynamic_viewport: bool, - dynamic_scissor: bool, - dynamic_depth_bias: bool, - dynamic_depth_bounds: bool, - dynamic_stencil_compare_mask: bool, - dynamic_stencil_write_mask: bool, - dynamic_stencil_reference: bool, - dynamic_blend_constants: bool, - + dynamic_state: FnvHashMap, num_viewports: u32, } @@ -107,52 +98,19 @@ impl GraphicsPipeline { self.num_viewports } - /// Returns true if the blend constants used by this pipeline are dynamic. - #[inline] - pub fn has_dynamic_blend_constants(&self) -> bool { - self.dynamic_blend_constants + /// Returns the mode of a particular dynamic state. + /// + /// `None` is returned if the pipeline does not contain this state. Previously set dynamic + /// state is not disturbed when binding it. + pub fn dynamic_state(&self, state: DynamicState) -> Option { + self.dynamic_state.get(&state).copied() } - /// Returns true if the depth bounds used by this pipeline are dynamic. - #[inline] - pub fn has_dynamic_depth_bounds(&self) -> bool { - self.dynamic_depth_bounds - } - - /// Returns true if the line width used by this pipeline is dynamic. - #[inline] - pub fn has_dynamic_line_width(&self) -> bool { - self.dynamic_line_width - } - - /// Returns true if the scissors used by this pipeline are dynamic. - #[inline] - pub fn has_dynamic_scissor(&self) -> bool { - self.dynamic_scissor - } - - /// Returns true if the stencil compare masks used by this pipeline are dynamic. - #[inline] - pub fn has_dynamic_stencil_compare_mask(&self) -> bool { - self.dynamic_stencil_compare_mask - } - - /// Returns true if the stencil references used by this pipeline are dynamic. - #[inline] - pub fn has_dynamic_stencil_reference(&self) -> bool { - self.dynamic_stencil_reference - } - - /// Returns true if the stencil write masks used by this pipeline are dynamic. - #[inline] - pub fn has_dynamic_stencil_write_mask(&self) -> bool { - self.dynamic_stencil_write_mask - } - - /// Returns true if the viewports used by this pipeline are dynamic. - #[inline] - pub fn has_dynamic_viewport(&self) -> bool { - self.dynamic_viewport + /// Returns all dynamic states and their modes. + pub fn dynamic_states( + &self, + ) -> impl ExactSizeIterator + '_ { + self.dynamic_state.iter().map(|(k, v)| (*k, *v)) } } @@ -218,3 +176,66 @@ unsafe impl<'a> VulkanObject for GraphicsPipelineSys<'a> { self.0 } } + +/// A particular state value within a graphics pipeline that can be dynamically set by a command +/// buffer. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(i32)] +pub enum DynamicState { + Viewport = ash::vk::DynamicState::VIEWPORT.as_raw(), + Scissor = ash::vk::DynamicState::SCISSOR.as_raw(), + LineWidth = ash::vk::DynamicState::LINE_WIDTH.as_raw(), + DepthBias = ash::vk::DynamicState::DEPTH_BIAS.as_raw(), + BlendConstants = ash::vk::DynamicState::BLEND_CONSTANTS.as_raw(), + DepthBounds = ash::vk::DynamicState::DEPTH_BOUNDS.as_raw(), + StencilCompareMask = ash::vk::DynamicState::STENCIL_COMPARE_MASK.as_raw(), + StencilWriteMask = ash::vk::DynamicState::STENCIL_WRITE_MASK.as_raw(), + StencilReference = ash::vk::DynamicState::STENCIL_REFERENCE.as_raw(), + ViewportWScaling = ash::vk::DynamicState::VIEWPORT_W_SCALING_NV.as_raw(), + DiscardRectangle = ash::vk::DynamicState::DISCARD_RECTANGLE_EXT.as_raw(), + SampleLocations = ash::vk::DynamicState::SAMPLE_LOCATIONS_EXT.as_raw(), + RayTracingPipelineStackSize = + ash::vk::DynamicState::RAY_TRACING_PIPELINE_STACK_SIZE_KHR.as_raw(), + ViewportShadingRatePalette = ash::vk::DynamicState::VIEWPORT_SHADING_RATE_PALETTE_NV.as_raw(), + ViewportCoarseSampleOrder = ash::vk::DynamicState::VIEWPORT_COARSE_SAMPLE_ORDER_NV.as_raw(), + ExclusiveScissor = ash::vk::DynamicState::EXCLUSIVE_SCISSOR_NV.as_raw(), + FragmentShadingRate = ash::vk::DynamicState::FRAGMENT_SHADING_RATE_KHR.as_raw(), + LineStipple = ash::vk::DynamicState::LINE_STIPPLE_EXT.as_raw(), + CullMode = ash::vk::DynamicState::CULL_MODE_EXT.as_raw(), + FrontFace = ash::vk::DynamicState::FRONT_FACE_EXT.as_raw(), + PrimitiveTopology = ash::vk::DynamicState::PRIMITIVE_TOPOLOGY_EXT.as_raw(), + ViewportWithCount = ash::vk::DynamicState::VIEWPORT_WITH_COUNT_EXT.as_raw(), + ScissorWithCount = ash::vk::DynamicState::SCISSOR_WITH_COUNT_EXT.as_raw(), + VertexInputBindingStride = ash::vk::DynamicState::VERTEX_INPUT_BINDING_STRIDE_EXT.as_raw(), + DepthTestEnable = ash::vk::DynamicState::DEPTH_TEST_ENABLE_EXT.as_raw(), + DepthWriteEnable = ash::vk::DynamicState::DEPTH_WRITE_ENABLE_EXT.as_raw(), + DepthCompareOp = ash::vk::DynamicState::DEPTH_COMPARE_OP_EXT.as_raw(), + DepthBoundsTestEnable = ash::vk::DynamicState::DEPTH_BOUNDS_TEST_ENABLE_EXT.as_raw(), + StencilTestEnable = ash::vk::DynamicState::STENCIL_TEST_ENABLE_EXT.as_raw(), + StencilOp = ash::vk::DynamicState::STENCIL_OP_EXT.as_raw(), + VertexInput = ash::vk::DynamicState::VERTEX_INPUT_EXT.as_raw(), + PatchControlPoints = ash::vk::DynamicState::PATCH_CONTROL_POINTS_EXT.as_raw(), + RasterizerDiscardEnable = ash::vk::DynamicState::RASTERIZER_DISCARD_ENABLE_EXT.as_raw(), + DepthBiasEnable = ash::vk::DynamicState::DEPTH_BIAS_ENABLE_EXT.as_raw(), + LogicOp = ash::vk::DynamicState::LOGIC_OP_EXT.as_raw(), + PrimitiveRestartEnable = ash::vk::DynamicState::PRIMITIVE_RESTART_ENABLE_EXT.as_raw(), + ColorWriteEnable = ash::vk::DynamicState::COLOR_WRITE_ENABLE_EXT.as_raw(), +} + +impl From for ash::vk::DynamicState { + #[inline] + fn from(val: DynamicState) -> Self { + Self::from_raw(val as i32) + } +} + +/// Specifies how a dynamic state is handled by a graphics pipeline. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DynamicStateMode { + /// The pipeline has a fixed value for this state. Previously set dynamic state will be lost + /// when binding it, and will have to be re-set after binding a pipeline that uses it. + Fixed, + /// The pipeline expects a dynamic value to be set by a command buffer. Previously set dynamic + /// state is not disturbed when binding it. + Dynamic, +} diff --git a/vulkano/src/pipeline/mod.rs b/vulkano/src/pipeline/mod.rs index 9b9b3e719..b34008085 100644 --- a/vulkano/src/pipeline/mod.rs +++ b/vulkano/src/pipeline/mod.rs @@ -78,6 +78,8 @@ pub use self::compute_pipeline::ComputePipeline; pub use self::compute_pipeline::ComputePipelineCreationError; pub use self::compute_pipeline::ComputePipelineSys; +pub use self::graphics_pipeline::DynamicState; +pub use self::graphics_pipeline::DynamicStateMode; pub use self::graphics_pipeline::GraphicsPipeline; pub use self::graphics_pipeline::GraphicsPipelineBuilder; pub use self::graphics_pipeline::GraphicsPipelineCreationError; diff --git a/vulkano/src/sampler.rs b/vulkano/src/sampler.rs index 2664a51d6..f5521943f 100644 --- a/vulkano/src/sampler.rs +++ b/vulkano/src/sampler.rs @@ -65,7 +65,7 @@ use crate::check_errors; use crate::device::Device; use crate::device::DeviceOwned; -pub use crate::pipeline::depth_stencil::Compare; +use crate::pipeline::depth_stencil::CompareOp; use crate::Error; use crate::OomError; use crate::VulkanObject; @@ -225,7 +225,7 @@ impl Sampler { max_anisotropy: f32, min_lod: f32, max_lod: f32, - compare: Compare, + compare: CompareOp, ) -> Result, SamplerCreationError> { Sampler::new_impl( device, @@ -255,7 +255,7 @@ impl Sampler { max_anisotropy: f32, min_lod: f32, max_lod: f32, - compare: Option, + compare: Option, ) -> Result, SamplerCreationError> { assert!(max_anisotropy >= 1.0); assert!(min_lod <= max_lod); @@ -854,7 +854,7 @@ mod tests { 1.0, 0.0, 2.0, - sampler::Compare::Less, + sampler::CompareOp::Less, ) .unwrap();