diff options
Diffstat (limited to 'src/command_buffer/commands/dynamic_state.rs')
-rw-r--r-- | src/command_buffer/commands/dynamic_state.rs | 3006 |
1 files changed, 3006 insertions, 0 deletions
diff --git a/src/command_buffer/commands/dynamic_state.rs b/src/command_buffer/commands/dynamic_state.rs new file mode 100644 index 0000000..b56df7f --- /dev/null +++ b/src/command_buffer/commands/dynamic_state.rs @@ -0,0 +1,3006 @@ +// Copyright (c) 2022 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use crate::{ + command_buffer::{ + allocator::CommandBufferAllocator, + synced::{Command, SyncCommandBufferBuilder}, + sys::UnsafeCommandBufferBuilder, + AutoCommandBufferBuilder, + }, + device::{DeviceOwned, QueueFlags}, + pipeline::{ + graphics::{ + color_blend::LogicOp, + depth_stencil::{CompareOp, StencilFaces, StencilOp, StencilOps}, + input_assembly::PrimitiveTopology, + rasterization::{CullMode, DepthBias, FrontFace, LineStipple}, + viewport::{Scissor, Viewport}, + }, + DynamicState, + }, + RequirementNotMet, RequiresOneOf, Version, +}; +use parking_lot::Mutex; +use smallvec::SmallVec; +use std::{ + error::Error, + fmt::{Display, Error as FmtError, Formatter}, + ops::RangeInclusive, +}; + +/// # Commands to set dynamic state for pipelines. +/// +/// These commands require a queue with a pipeline type that uses the given state. +impl<L, A> AutoCommandBufferBuilder<L, A> +where + A: CommandBufferAllocator, +{ + // Helper function for dynamic state setting. + fn validate_pipeline_fixed_state( + &self, + state: DynamicState, + ) -> Result<(), SetDynamicStateError> { + // VUID-vkCmdDispatch-None-02859 + if self.state().pipeline_graphics().map_or(false, |pipeline| { + matches!(pipeline.dynamic_state(state), Some(false)) + }) { + return Err(SetDynamicStateError::PipelineHasFixedState); + } + + Ok(()) + } + + /// 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 { + self.validate_set_blend_constants(constants).unwrap(); + + unsafe { + self.inner.set_blend_constants(constants); + } + + self + } + + fn validate_set_blend_constants( + &self, + _constants: [f32; 4], + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::BlendConstants)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetBlendConstants-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + Ok(()) + } + + /// Sets whether dynamic color writes should be enabled for each attachment in the + /// framebuffer. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the [`color_write_enable`](crate::device::Features::color_write_enable) + /// feature is not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + /// - If there is a graphics pipeline with color blend state bound, `enables.len()` must equal + /// - [`attachments.len()`](crate::pipeline::graphics::color_blend::ColorBlendState::attachments). + pub fn set_color_write_enable<I>(&mut self, enables: I) -> &mut Self + where + I: IntoIterator<Item = bool>, + I::IntoIter: ExactSizeIterator, + { + let enables = enables.into_iter(); + + self.validate_set_color_write_enable(&enables).unwrap(); + + unsafe { + self.inner.set_color_write_enable(enables); + } + + self + } + + fn validate_set_color_write_enable( + &self, + enables: &impl ExactSizeIterator, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::ColorWriteEnable)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetColorWriteEnableEXT-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetColorWriteEnableEXT-None-04803 + if !self.device().enabled_features().color_write_enable { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_color_write_enable`", + requires_one_of: RequiresOneOf { + device_extensions: &["ext_color_write_enable"], + ..Default::default() + }, + }); + } + + if let Some(color_blend_state) = self + .state() + .pipeline_graphics() + .and_then(|pipeline| pipeline.color_blend_state()) + { + // VUID-vkCmdSetColorWriteEnableEXT-attachmentCount-06656 + // Indirectly checked + if enables.len() != color_blend_state.attachments.len() { + return Err( + SetDynamicStateError::PipelineColorBlendAttachmentCountMismatch { + provided_count: enables.len() as u32, + required_count: color_blend_state.attachments.len() as u32, + }, + ); + } + } + + Ok(()) + } + + /// Sets the dynamic cull mode for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_cull_mode(&mut self, cull_mode: CullMode) -> &mut Self { + self.validate_set_cull_mode(cull_mode).unwrap(); + + unsafe { + self.inner.set_cull_mode(cull_mode); + } + + self + } + + fn validate_set_cull_mode(&self, cull_mode: CullMode) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::CullMode)?; + + // VUID-vkCmdSetCullMode-cullMode-parameter + cull_mode.validate_device(self.device())?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetCullMode-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetCullMode-None-03384 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_cull_mode`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic depth bias values 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. + /// - If the [`depth_bias_clamp`](crate::device::Features::depth_bias_clamp) + /// feature is not enabled on the device, panics if `clamp` is not 0.0. + pub fn set_depth_bias( + &mut self, + constant_factor: f32, + clamp: f32, + slope_factor: f32, + ) -> &mut Self { + self.validate_set_depth_bias(constant_factor, clamp, slope_factor) + .unwrap(); + + unsafe { + self.inner + .set_depth_bias(constant_factor, clamp, slope_factor); + } + + self + } + + fn validate_set_depth_bias( + &self, + _constant_factor: f32, + clamp: f32, + _slope_factor: f32, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::DepthBias)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetDepthBias-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetDepthBias-depthBiasClamp-00790 + if clamp != 0.0 && !self.device().enabled_features().depth_bias_clamp { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`clamp` is not `0.0`", + requires_one_of: RequiresOneOf { + features: &["depth_bias_clamp"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets whether dynamic depth bias is enabled for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_depth_bias_enable(&mut self, enable: bool) -> &mut Self { + self.validate_set_depth_bias_enable(enable).unwrap(); + + unsafe { + self.inner.set_depth_bias_enable(enable); + } + + self + } + + fn validate_set_depth_bias_enable(&self, _enable: bool) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::DepthBiasEnable)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetDepthBiasEnable-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetDepthBiasEnable-None-04872 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state2) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_depth_bias_enable`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state2"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic depth bounds 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. + /// - If the + /// [`ext_depth_range_unrestricted`](crate::device::DeviceExtensions::ext_depth_range_unrestricted) + /// device extension is not enabled, panics if the start and end of `bounds` are not between + /// 0.0 and 1.0 inclusive. + pub fn set_depth_bounds(&mut self, bounds: RangeInclusive<f32>) -> &mut Self { + self.validate_set_depth_bounds(bounds.clone()).unwrap(); + + unsafe { + self.inner.set_depth_bounds(bounds); + } + + self + } + + fn validate_set_depth_bounds( + &self, + bounds: RangeInclusive<f32>, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::DepthBounds)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetDepthBounds-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetDepthBounds-minDepthBounds-00600 + // VUID-vkCmdSetDepthBounds-maxDepthBounds-00601 + if !self + .device() + .enabled_extensions() + .ext_depth_range_unrestricted + && !((0.0..=1.0).contains(bounds.start()) && (0.0..=1.0).contains(bounds.end())) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`bounds` is not between `0.0` and `1.0` inclusive", + requires_one_of: RequiresOneOf { + device_extensions: &["ext_depth_range_unrestricted"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets whether dynamic depth bounds testing is enabled for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_depth_bounds_test_enable(&mut self, enable: bool) -> &mut Self { + self.validate_set_depth_bounds_test_enable(enable).unwrap(); + + unsafe { + self.inner.set_depth_bounds_test_enable(enable); + } + + self + } + + fn validate_set_depth_bounds_test_enable( + &self, + _enable: bool, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::DepthBoundsTestEnable)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetDepthBoundsTestEnable-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetDepthBoundsTestEnable-None-03349 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_depth_bounds_test_enable`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic depth compare op for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_depth_compare_op(&mut self, compare_op: CompareOp) -> &mut Self { + self.validate_set_depth_compare_op(compare_op).unwrap(); + + unsafe { + self.inner.set_depth_compare_op(compare_op); + } + + self + } + + fn validate_set_depth_compare_op( + &self, + compare_op: CompareOp, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::DepthCompareOp)?; + + // VUID-vkCmdSetDepthCompareOp-depthCompareOp-parameter + compare_op.validate_device(self.device())?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetDepthCompareOp-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetDepthCompareOp-None-03353 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_depth_compare_op`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets whether dynamic depth testing is enabled for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_depth_test_enable(&mut self, enable: bool) -> &mut Self { + self.validate_set_depth_test_enable(enable).unwrap(); + + unsafe { + self.inner.set_depth_test_enable(enable); + } + + self + } + + fn validate_set_depth_test_enable(&self, _enable: bool) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::DepthTestEnable)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetDepthTestEnable-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetDepthTestEnable-None-03352 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_depth_test_enable`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets whether dynamic depth write is enabled for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_depth_write_enable(&mut self, enable: bool) -> &mut Self { + self.validate_set_depth_write_enable(enable).unwrap(); + + unsafe { + self.inner.set_depth_write_enable(enable); + } + + self + } + + fn validate_set_depth_write_enable(&self, _enable: bool) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::DepthWriteEnable)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetDepthWriteEnable-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetDepthWriteEnable-None-03354 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_depth_write_enable`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic discard rectangles for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the + /// [`ext_discard_rectangles`](crate::device::DeviceExtensions::ext_discard_rectangles) + /// extension is not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + /// - Panics if the highest discard rectangle slot being set is greater than the + /// [`max_discard_rectangles`](crate::device::Properties::max_discard_rectangles) device + /// property. + pub fn set_discard_rectangle( + &mut self, + first_rectangle: u32, + rectangles: impl IntoIterator<Item = Scissor>, + ) -> &mut Self { + let rectangles: SmallVec<[Scissor; 2]> = rectangles.into_iter().collect(); + self.validate_set_discard_rectangle(first_rectangle, &rectangles) + .unwrap(); + + unsafe { + self.inner + .set_discard_rectangle(first_rectangle, rectangles); + } + + self + } + + fn validate_set_discard_rectangle( + &self, + first_rectangle: u32, + rectangles: &[Scissor], + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::DiscardRectangle)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetDiscardRectangle-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + if self.device().enabled_extensions().ext_discard_rectangles { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_discard_rectangle`", + requires_one_of: RequiresOneOf { + device_extensions: &["ext_discard_rectangles"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdSetDiscardRectangleEXT-firstDiscardRectangle-00585 + if first_rectangle + rectangles.len() as u32 + > self + .device() + .physical_device() + .properties() + .max_discard_rectangles + .unwrap() + { + return Err(SetDynamicStateError::MaxDiscardRectanglesExceeded { + provided: first_rectangle + rectangles.len() as u32, + max: self + .device() + .physical_device() + .properties() + .max_discard_rectangles + .unwrap(), + }); + } + + Ok(()) + } + + /// Sets the dynamic front face for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_front_face(&mut self, face: FrontFace) -> &mut Self { + self.validate_set_front_face(face).unwrap(); + + unsafe { + self.inner.set_front_face(face); + } + + self + } + + fn validate_set_front_face(&self, face: FrontFace) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::FrontFace)?; + + // VUID-vkCmdSetFrontFace-frontFace-parameter + face.validate_device(self.device())?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetFrontFace-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetFrontFace-None-03383 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_front_face`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic line stipple values for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the [`ext_line_rasterization`](crate::device::DeviceExtensions::ext_line_rasterization) + /// extension is not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + /// - Panics if `factor` is not between 1 and 256 inclusive. + pub fn set_line_stipple(&mut self, factor: u32, pattern: u16) -> &mut Self { + self.validate_set_line_stipple(factor, pattern).unwrap(); + + unsafe { + self.inner.set_line_stipple(factor, pattern); + } + + self + } + + fn validate_set_line_stipple( + &self, + factor: u32, + _pattern: u16, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::LineStipple)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetLineStippleEXT-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + if !self.device().enabled_extensions().ext_line_rasterization { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_line_stipple`", + requires_one_of: RequiresOneOf { + device_extensions: &["ext_line_rasterization"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdSetLineStippleEXT-lineStippleFactor-02776 + if !(1..=256).contains(&factor) { + return Err(SetDynamicStateError::FactorOutOfRange); + } + + Ok(()) + } + + /// Sets the dynamic line width 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. + /// - 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 { + self.validate_set_line_width(line_width).unwrap(); + + unsafe { + self.inner.set_line_width(line_width); + } + + self + } + + fn validate_set_line_width(&self, line_width: f32) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::LineWidth)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetLineWidth-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetLineWidth-lineWidth-00788 + if !self.device().enabled_features().wide_lines && line_width != 1.0 { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`line_width` is not `1.0`", + requires_one_of: RequiresOneOf { + features: &["wide_lines"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic logic op for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the + /// [`extended_dynamic_state2_logic_op`](crate::device::Features::extended_dynamic_state2_logic_op) + /// feature is not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_logic_op(&mut self, logic_op: LogicOp) -> &mut Self { + self.validate_set_logic_op(logic_op).unwrap(); + + unsafe { + self.inner.set_logic_op(logic_op); + } + + self + } + + fn validate_set_logic_op(&self, logic_op: LogicOp) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::LogicOp)?; + + // VUID-vkCmdSetLogicOpEXT-logicOp-parameter + logic_op.validate_device(self.device())?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetLogicOpEXT-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetLogicOpEXT-None-04867 + if !self + .device() + .enabled_features() + .extended_dynamic_state2_logic_op + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_logic_op`", + requires_one_of: RequiresOneOf { + features: &["extended_dynamic_state2_logic_op"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic number of patch control points for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the + /// [`extended_dynamic_state2_patch_control_points`](crate::device::Features::extended_dynamic_state2_patch_control_points) + /// feature is not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + /// - Panics if `num` is 0. + /// - Panics if `num` is greater than the + /// [`max_tessellation_patch_size`](crate::device::Properties::max_tessellation_patch_size) + /// property of the device. + pub fn set_patch_control_points(&mut self, num: u32) -> &mut Self { + self.validate_set_patch_control_points(num).unwrap(); + + unsafe { + self.inner.set_patch_control_points(num); + } + + self + } + + fn validate_set_patch_control_points(&self, num: u32) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::PatchControlPoints)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetPatchControlPointsEXT-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetPatchControlPointsEXT-None-04873 + if !self + .device() + .enabled_features() + .extended_dynamic_state2_patch_control_points + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_patch_control_points`", + requires_one_of: RequiresOneOf { + features: &["extended_dynamic_state2_patch_control_points"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdSetPatchControlPointsEXT-patchControlPoints-04874 + assert!(num > 0, "num must be greater than 0"); + + // VUID-vkCmdSetPatchControlPointsEXT-patchControlPoints-04874 + if num + > self + .device() + .physical_device() + .properties() + .max_tessellation_patch_size + { + return Err(SetDynamicStateError::MaxTessellationPatchSizeExceeded { + provided: num, + max: self + .device() + .physical_device() + .properties() + .max_tessellation_patch_size, + }); + } + + Ok(()) + } + + /// Sets whether dynamic primitive restart is enabled for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_primitive_restart_enable(&mut self, enable: bool) -> &mut Self { + self.validate_set_primitive_restart_enable(enable).unwrap(); + + unsafe { + self.inner.set_primitive_restart_enable(enable); + } + + self + } + + fn validate_set_primitive_restart_enable( + &self, + _enable: bool, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::PrimitiveRestartEnable)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetPrimitiveRestartEnable-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetPrimitiveRestartEnable-None-04866 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state2) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_primitive_restart_enable`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state2"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic primitive topology for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + /// - If the [`geometry_shader`](crate::device::Features::geometry_shader) feature is not + /// enabled, panics if `topology` is a `WithAdjacency` topology. + /// - If the [`tessellation_shader`](crate::device::Features::tessellation_shader) feature is + /// not enabled, panics if `topology` is `PatchList`. + pub fn set_primitive_topology(&mut self, topology: PrimitiveTopology) -> &mut Self { + self.validate_set_primitive_topology(topology).unwrap(); + + unsafe { + self.inner.set_primitive_topology(topology); + } + + self + } + + fn validate_set_primitive_topology( + &self, + topology: PrimitiveTopology, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::PrimitiveTopology)?; + + // VUID-vkCmdSetPrimitiveTopology-primitiveTopology-parameter + topology.validate_device(self.device())?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetPrimitiveTopology-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetPrimitiveTopology-None-03347 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_primitive_topology`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + // VUID? + // Since these requirements exist for fixed state when creating the pipeline, + // I assume they exist for dynamic state as well. + match topology { + PrimitiveTopology::TriangleFan => { + if self.device().enabled_extensions().khr_portability_subset + && !self.device().enabled_features().triangle_fans + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "this device is a portability subset device, and `topology` \ + is `PrimitiveTopology::TriangleFan`", + requires_one_of: RequiresOneOf { + features: &["triangle_fans"], + ..Default::default() + }, + }); + } + } + PrimitiveTopology::LineListWithAdjacency + | PrimitiveTopology::LineStripWithAdjacency + | PrimitiveTopology::TriangleListWithAdjacency + | PrimitiveTopology::TriangleStripWithAdjacency => { + if !self.device().enabled_features().geometry_shader { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`topology` is `PrimitiveTopology::*WithAdjacency`", + requires_one_of: RequiresOneOf { + features: &["geometry_shader"], + ..Default::default() + }, + }); + } + } + PrimitiveTopology::PatchList => { + if !self.device().enabled_features().tessellation_shader { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`topology` is `PrimitiveTopology::PatchList`", + requires_one_of: RequiresOneOf { + features: &["tessellation_shader"], + ..Default::default() + }, + }); + } + } + _ => (), + } + + Ok(()) + } + + /// Sets whether dynamic rasterizer discard is enabled for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_rasterizer_discard_enable(&mut self, enable: bool) -> &mut Self { + self.validate_set_rasterizer_discard_enable(enable).unwrap(); + + unsafe { + self.inner.set_rasterizer_discard_enable(enable); + } + + self + } + + fn validate_set_rasterizer_discard_enable( + &self, + _enable: bool, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::RasterizerDiscardEnable)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetRasterizerDiscardEnable-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetRasterizerDiscardEnable-None-04871 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state2) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_rasterizer_discard_enable`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state2"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic scissors 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. + /// - 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, + /// panics if `first_scissor` is not 0, or if more than 1 scissor is provided. + pub fn set_scissor( + &mut self, + first_scissor: u32, + scissors: impl IntoIterator<Item = Scissor>, + ) -> &mut Self { + let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect(); + self.validate_set_scissor(first_scissor, &scissors).unwrap(); + + unsafe { + self.inner.set_scissor(first_scissor, scissors); + } + + self + } + + fn validate_set_scissor( + &self, + first_scissor: u32, + scissors: &[Scissor], + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::Scissor)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetScissor-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetScissor-firstScissor-00592 + if first_scissor + scissors.len() as u32 + > self.device().physical_device().properties().max_viewports + { + return Err(SetDynamicStateError::MaxViewportsExceeded { + provided: first_scissor + scissors.len() as u32, + max: self.device().physical_device().properties().max_viewports, + }); + } + + if !self.device().enabled_features().multi_viewport { + // VUID-vkCmdSetScissor-firstScissor-00593 + if first_scissor != 0 { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`first_scissor` is not `0`", + requires_one_of: RequiresOneOf { + features: &["multi_viewport"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdSetScissor-scissorCount-00594 + if scissors.len() > 1 { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`scissors.len()` is greater than `1`", + requires_one_of: RequiresOneOf { + features: &["multi_viewport"], + ..Default::default() + }, + }); + } + } + + Ok(()) + } + + /// Sets the dynamic scissors with count for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - 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, + /// panics if more than 1 scissor is provided. + pub fn set_scissor_with_count( + &mut self, + scissors: impl IntoIterator<Item = Scissor>, + ) -> &mut Self { + let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect(); + self.validate_set_scissor_with_count(&scissors).unwrap(); + + unsafe { + self.inner.set_scissor_with_count(scissors); + } + + self + } + + fn validate_set_scissor_with_count( + &self, + scissors: &[Scissor], + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::ScissorWithCount)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetScissorWithCount-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetScissorWithCount-None-03396 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_scissor_with_count`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdSetScissorWithCount-scissorCount-03397 + if scissors.len() as u32 > self.device().physical_device().properties().max_viewports { + return Err(SetDynamicStateError::MaxViewportsExceeded { + provided: scissors.len() as u32, + max: self.device().physical_device().properties().max_viewports, + }); + } + + // VUID-vkCmdSetScissorWithCount-scissorCount-03398 + if !self.device().enabled_features().multi_viewport && scissors.len() > 1 { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`scissors.len()` is greater than `1`", + requires_one_of: RequiresOneOf { + features: &["multi_viewport"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic stencil compare mask on one or both faces 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_stencil_compare_mask( + &mut self, + faces: StencilFaces, + compare_mask: u32, + ) -> &mut Self { + self.validate_set_stencil_compare_mask(faces, compare_mask) + .unwrap(); + + unsafe { + self.inner.set_stencil_compare_mask(faces, compare_mask); + } + + self + } + + fn validate_set_stencil_compare_mask( + &self, + faces: StencilFaces, + _compare_mask: u32, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::StencilCompareMask)?; + + // VUID-vkCmdSetStencilCompareMask-faceMask-parameter + faces.validate_device(self.device())?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetStencilCompareMask-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + Ok(()) + } + + /// Sets the dynamic stencil ops on one or both faces for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_stencil_op( + &mut self, + faces: StencilFaces, + fail_op: StencilOp, + pass_op: StencilOp, + depth_fail_op: StencilOp, + compare_op: CompareOp, + ) -> &mut Self { + self.validate_set_stencil_op(faces, fail_op, pass_op, depth_fail_op, compare_op) + .unwrap(); + + unsafe { + self.inner + .set_stencil_op(faces, fail_op, pass_op, depth_fail_op, compare_op); + } + + self + } + + fn validate_set_stencil_op( + &self, + faces: StencilFaces, + fail_op: StencilOp, + pass_op: StencilOp, + depth_fail_op: StencilOp, + compare_op: CompareOp, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::StencilOp)?; + + // VUID-vkCmdSetStencilOp-faceMask-parameter + faces.validate_device(self.device())?; + + // VUID-vkCmdSetStencilOp-failOp-parameter + fail_op.validate_device(self.device())?; + + // VUID-vkCmdSetStencilOp-passOp-parameter + pass_op.validate_device(self.device())?; + + // VUID-vkCmdSetStencilOp-depthFailOp-parameter + depth_fail_op.validate_device(self.device())?; + + // VUID-vkCmdSetStencilOp-compareOp-parameter + compare_op.validate_device(self.device())?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetStencilOp-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetStencilOp-None-03351 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_stencil_op`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic stencil reference on one or both faces 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_stencil_reference(&mut self, faces: StencilFaces, reference: u32) -> &mut Self { + self.validate_set_stencil_reference(faces, reference) + .unwrap(); + + unsafe { + self.inner.set_stencil_reference(faces, reference); + } + + self + } + + fn validate_set_stencil_reference( + &self, + faces: StencilFaces, + _reference: u32, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::StencilReference)?; + + // VUID-vkCmdSetStencilReference-faceMask-parameter + faces.validate_device(self.device())?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetStencilReference-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + Ok(()) + } + + /// Sets whether dynamic stencil testing is enabled for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - Panics if the currently bound graphics pipeline already contains this state internally. + pub fn set_stencil_test_enable(&mut self, enable: bool) -> &mut Self { + self.validate_set_stencil_test_enable(enable).unwrap(); + + unsafe { + self.inner.set_stencil_test_enable(enable); + } + + self + } + + fn validate_set_stencil_test_enable(&self, _enable: bool) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::StencilTestEnable)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetStencilTestEnable-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetStencilTestEnable-None-03350 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_stencil_test_enable`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + Ok(()) + } + + /// Sets the dynamic stencil write mask on one or both faces 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_stencil_write_mask(&mut self, faces: StencilFaces, write_mask: u32) -> &mut Self { + self.validate_set_stencil_write_mask(faces, write_mask) + .unwrap(); + + unsafe { + self.inner.set_stencil_write_mask(faces, write_mask); + } + + self + } + + fn validate_set_stencil_write_mask( + &self, + faces: StencilFaces, + _write_mask: u32, + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::StencilWriteMask)?; + + // VUID-vkCmdSetStencilWriteMask-faceMask-parameter + faces.validate_device(self.device())?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetStencilWriteMask-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + Ok(()) + } + + /// Sets the dynamic viewports 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. + /// - 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, + /// panics if `first_viewport` is not 0, or if more than 1 viewport is provided. + pub fn set_viewport( + &mut self, + first_viewport: u32, + viewports: impl IntoIterator<Item = Viewport>, + ) -> &mut Self { + let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect(); + self.validate_set_viewport(first_viewport, &viewports) + .unwrap(); + + unsafe { + self.inner.set_viewport(first_viewport, viewports); + } + + self + } + + fn validate_set_viewport( + &self, + first_viewport: u32, + viewports: &[Viewport], + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::Viewport)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetViewport-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetViewport-firstViewport-01223 + if first_viewport + viewports.len() as u32 + > self.device().physical_device().properties().max_viewports + { + return Err(SetDynamicStateError::MaxViewportsExceeded { + provided: first_viewport + viewports.len() as u32, + max: self.device().physical_device().properties().max_viewports, + }); + } + + if !self.device().enabled_features().multi_viewport { + // VUID-vkCmdSetViewport-firstViewport-01224 + if first_viewport != 0 { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`first_scissors` is not `0`", + requires_one_of: RequiresOneOf { + features: &["multi_viewport"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdSetViewport-viewportCount-01225 + if viewports.len() > 1 { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`viewports.len()` is greater than `1`", + requires_one_of: RequiresOneOf { + features: &["multi_viewport"], + ..Default::default() + }, + }); + } + } + + Ok(()) + } + + /// Sets the dynamic viewports with count for future draw calls. + /// + /// # Panics + /// + /// - Panics if the queue family of the command buffer does not support graphics operations. + /// - Panics if the device API version is less than 1.3 and the + /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is + /// not enabled on the device. + /// - 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, + /// panics if more than 1 viewport is provided. + pub fn set_viewport_with_count( + &mut self, + viewports: impl IntoIterator<Item = Viewport>, + ) -> &mut Self { + let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect(); + self.validate_set_viewport_with_count(&viewports).unwrap(); + + unsafe { + self.inner.set_viewport_with_count(viewports); + } + + self + } + + fn validate_set_viewport_with_count( + &self, + viewports: &[Viewport], + ) -> Result<(), SetDynamicStateError> { + self.validate_pipeline_fixed_state(DynamicState::ViewportWithCount)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetViewportWithCount-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(SetDynamicStateError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetViewportWithCount-None-03393 + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`AutoCommandBufferBuilder::set_viewport_with_count`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_3), + features: &["extended_dynamic_state"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdSetViewportWithCount-viewportCount-03394 + if viewports.len() as u32 > self.device().physical_device().properties().max_viewports { + return Err(SetDynamicStateError::MaxViewportsExceeded { + provided: viewports.len() as u32, + max: self.device().physical_device().properties().max_viewports, + }); + } + + // VUID-vkCmdSetViewportWithCount-viewportCount-03395 + if !self.device().enabled_features().multi_viewport && viewports.len() > 1 { + return Err(SetDynamicStateError::RequirementNotMet { + required_for: "`viewports.len()` is greater than `1`", + requires_one_of: RequiresOneOf { + features: &["multi_viewport"], + ..Default::default() + }, + }); + } + + Ok(()) + } +} + +impl SyncCommandBufferBuilder { + /// Calls `vkCmdSetBlendConstants` on the builder. + #[inline] + pub unsafe fn set_blend_constants(&mut self, constants: [f32; 4]) { + struct Cmd { + constants: [f32; 4], + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_blend_constants" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_blend_constants(self.constants); + } + } + + self.commands.push(Box::new(Cmd { constants })); + self.current_state.blend_constants = Some(constants); + } + + /// Calls `vkCmdSetColorWriteEnableEXT` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_color_write_enable(&mut self, enables: impl IntoIterator<Item = bool>) { + struct Cmd<I> { + enables: Mutex<Option<I>>, + } + + impl<I> Command for Cmd<I> + where + I: IntoIterator<Item = bool> + Send + Sync, + { + fn name(&self) -> &'static str { + "set_color_write_enable" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_color_write_enable(self.enables.lock().take().unwrap()); + } + } + + let enables: SmallVec<[bool; 4]> = enables.into_iter().collect(); + self.current_state.color_write_enable = Some(enables.clone()); + self.commands.push(Box::new(Cmd { + enables: Mutex::new(Some(enables)), + })); + } + + /// Calls `vkCmdSetCullModeEXT` on the builder. + #[inline] + pub unsafe fn set_cull_mode(&mut self, cull_mode: CullMode) { + struct Cmd { + cull_mode: CullMode, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_cull_mode" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_cull_mode(self.cull_mode); + } + } + + self.commands.push(Box::new(Cmd { cull_mode })); + self.current_state.cull_mode = Some(cull_mode); + } + + /// Calls `vkCmdSetDepthBias` on the builder. + #[inline] + pub unsafe fn set_depth_bias(&mut self, constant_factor: f32, clamp: f32, slope_factor: f32) { + struct Cmd { + constant_factor: f32, + clamp: f32, + slope_factor: f32, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_depth_bias" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_depth_bias(self.constant_factor, self.clamp, self.slope_factor); + } + } + + self.commands.push(Box::new(Cmd { + constant_factor, + clamp, + slope_factor, + })); + self.current_state.depth_bias = Some(DepthBias { + constant_factor, + clamp, + slope_factor, + }); + } + + /// Calls `vkCmdSetDepthBiasEnableEXT` on the builder. + #[inline] + pub unsafe fn set_depth_bias_enable(&mut self, enable: bool) { + struct Cmd { + enable: bool, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_depth_bias_enable" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_depth_bias_enable(self.enable); + } + } + + self.commands.push(Box::new(Cmd { enable })); + self.current_state.depth_bias_enable = Some(enable); + } + + /// Calls `vkCmdSetDepthBounds` on the builder. + #[inline] + pub unsafe fn set_depth_bounds(&mut self, bounds: RangeInclusive<f32>) { + struct Cmd { + bounds: RangeInclusive<f32>, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_depth_bounds" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_depth_bounds(self.bounds.clone()); + } + } + + self.commands.push(Box::new(Cmd { + bounds: bounds.clone(), + })); + self.current_state.depth_bounds = Some(bounds); + } + + /// Calls `vkCmdSetDepthBoundsTestEnableEXT` on the builder. + #[inline] + pub unsafe fn set_depth_bounds_test_enable(&mut self, enable: bool) { + struct Cmd { + enable: bool, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_depth_bounds_test_enable" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_depth_bounds_test_enable(self.enable); + } + } + + self.commands.push(Box::new(Cmd { enable })); + self.current_state.depth_bounds_test_enable = Some(enable); + } + + /// Calls `vkCmdSetDepthCompareOpEXT` on the builder. + #[inline] + pub unsafe fn set_depth_compare_op(&mut self, compare_op: CompareOp) { + struct Cmd { + compare_op: CompareOp, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_depth_compare_op" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_depth_compare_op(self.compare_op); + } + } + + self.commands.push(Box::new(Cmd { compare_op })); + self.current_state.depth_compare_op = Some(compare_op); + } + + /// Calls `vkCmdSetDepthTestEnableEXT` on the builder. + #[inline] + pub unsafe fn set_depth_test_enable(&mut self, enable: bool) { + struct Cmd { + enable: bool, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_depth_test_enable" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_depth_test_enable(self.enable); + } + } + + self.commands.push(Box::new(Cmd { enable })); + self.current_state.depth_test_enable = Some(enable); + } + + /// Calls `vkCmdSetDepthWriteEnableEXT` on the builder. + #[inline] + pub unsafe fn set_depth_write_enable(&mut self, enable: bool) { + struct Cmd { + enable: bool, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_depth_write_enable" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_depth_write_enable(self.enable); + } + } + + self.commands.push(Box::new(Cmd { enable })); + self.current_state.depth_write_enable = Some(enable); + } + + /// Calls `vkCmdSetDiscardRectangle` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_discard_rectangle( + &mut self, + first_rectangle: u32, + rectangles: impl IntoIterator<Item = Scissor>, + ) { + struct Cmd { + first_rectangle: u32, + rectangles: Mutex<SmallVec<[Scissor; 2]>>, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_discard_rectangle" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_discard_rectangle(self.first_rectangle, self.rectangles.lock().drain(..)); + } + } + + let rectangles: SmallVec<[Scissor; 2]> = rectangles.into_iter().collect(); + + for (num, rectangle) in rectangles.iter().enumerate() { + let num = num as u32 + first_rectangle; + self.current_state.discard_rectangle.insert(num, *rectangle); + } + + self.commands.push(Box::new(Cmd { + first_rectangle, + rectangles: Mutex::new(rectangles), + })); + } + + /// Calls `vkCmdSetFrontFaceEXT` on the builder. + #[inline] + pub unsafe fn set_front_face(&mut self, face: FrontFace) { + struct Cmd { + face: FrontFace, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_front_face" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_front_face(self.face); + } + } + + self.commands.push(Box::new(Cmd { face })); + self.current_state.front_face = Some(face); + } + + /// Calls `vkCmdSetLineStippleEXT` on the builder. + #[inline] + pub unsafe fn set_line_stipple(&mut self, factor: u32, pattern: u16) { + struct Cmd { + factor: u32, + pattern: u16, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_line_stipple" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_line_stipple(self.factor, self.pattern); + } + } + + self.commands.push(Box::new(Cmd { factor, pattern })); + self.current_state.line_stipple = Some(LineStipple { factor, pattern }); + } + + /// Calls `vkCmdSetLineWidth` on the builder. + #[inline] + pub unsafe fn set_line_width(&mut self, line_width: f32) { + struct Cmd { + line_width: f32, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_line_width" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_line_width(self.line_width); + } + } + + self.commands.push(Box::new(Cmd { line_width })); + self.current_state.line_width = Some(line_width); + } + + /// Calls `vkCmdSetLogicOpEXT` on the builder. + #[inline] + pub unsafe fn set_logic_op(&mut self, logic_op: LogicOp) { + struct Cmd { + logic_op: LogicOp, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_logic_op" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_logic_op(self.logic_op); + } + } + + self.commands.push(Box::new(Cmd { logic_op })); + self.current_state.logic_op = Some(logic_op); + } + + /// Calls `vkCmdSetPatchControlPointsEXT` on the builder. + #[inline] + pub unsafe fn set_patch_control_points(&mut self, num: u32) { + struct Cmd { + num: u32, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_patch_control_points" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_patch_control_points(self.num); + } + } + + self.commands.push(Box::new(Cmd { num })); + self.current_state.patch_control_points = Some(num); + } + + /// Calls `vkCmdSetPrimitiveRestartEnableEXT` on the builder. + #[inline] + pub unsafe fn set_primitive_restart_enable(&mut self, enable: bool) { + struct Cmd { + enable: bool, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_primitive_restart_enable" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_primitive_restart_enable(self.enable); + } + } + + self.commands.push(Box::new(Cmd { enable })); + self.current_state.primitive_restart_enable = Some(enable); + } + + /// Calls `vkCmdSetPrimitiveTopologyEXT` on the builder. + #[inline] + pub unsafe fn set_primitive_topology(&mut self, topology: PrimitiveTopology) { + struct Cmd { + topology: PrimitiveTopology, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_primitive_topology" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_primitive_topology(self.topology); + } + } + + self.commands.push(Box::new(Cmd { topology })); + self.current_state.primitive_topology = Some(topology); + } + + /// Calls `vkCmdSetRasterizerDiscardEnableEXT` on the builder. + #[inline] + pub unsafe fn set_rasterizer_discard_enable(&mut self, enable: bool) { + struct Cmd { + enable: bool, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_rasterizer_discard_enable" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_rasterizer_discard_enable(self.enable); + } + } + + self.commands.push(Box::new(Cmd { enable })); + self.current_state.rasterizer_discard_enable = Some(enable); + } + + /// Calls `vkCmdSetStencilCompareMask` on the builder. + #[inline] + pub unsafe fn set_stencil_compare_mask(&mut self, faces: StencilFaces, compare_mask: u32) { + struct Cmd { + faces: StencilFaces, + compare_mask: u32, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_stencil_compare_mask" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_stencil_compare_mask(self.faces, self.compare_mask); + } + } + + self.commands.push(Box::new(Cmd { + faces, + compare_mask, + })); + + let faces = ash::vk::StencilFaceFlags::from(faces); + + if faces.intersects(ash::vk::StencilFaceFlags::FRONT) { + self.current_state.stencil_compare_mask.front = Some(compare_mask); + } + + if faces.intersects(ash::vk::StencilFaceFlags::BACK) { + self.current_state.stencil_compare_mask.back = Some(compare_mask); + } + } + + /// Calls `vkCmdSetStencilOpEXT` on the builder. + #[inline] + pub unsafe fn set_stencil_op( + &mut self, + faces: StencilFaces, + fail_op: StencilOp, + pass_op: StencilOp, + depth_fail_op: StencilOp, + compare_op: CompareOp, + ) { + struct Cmd { + faces: StencilFaces, + fail_op: StencilOp, + pass_op: StencilOp, + depth_fail_op: StencilOp, + compare_op: CompareOp, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_stencil_op" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_stencil_op( + self.faces, + self.fail_op, + self.pass_op, + self.depth_fail_op, + self.compare_op, + ); + } + } + + self.commands.push(Box::new(Cmd { + faces, + fail_op, + pass_op, + depth_fail_op, + compare_op, + })); + + let faces = ash::vk::StencilFaceFlags::from(faces); + + if faces.intersects(ash::vk::StencilFaceFlags::FRONT) { + self.current_state.stencil_op.front = Some(StencilOps { + fail_op, + pass_op, + depth_fail_op, + compare_op, + }); + } + + if faces.intersects(ash::vk::StencilFaceFlags::BACK) { + self.current_state.stencil_op.back = Some(StencilOps { + fail_op, + pass_op, + depth_fail_op, + compare_op, + }); + } + } + + /// Calls `vkCmdSetStencilReference` on the builder. + #[inline] + pub unsafe fn set_stencil_reference(&mut self, faces: StencilFaces, reference: u32) { + struct Cmd { + faces: StencilFaces, + reference: u32, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_stencil_reference" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_stencil_reference(self.faces, self.reference); + } + } + + self.commands.push(Box::new(Cmd { faces, reference })); + + let faces = ash::vk::StencilFaceFlags::from(faces); + + if faces.intersects(ash::vk::StencilFaceFlags::FRONT) { + self.current_state.stencil_reference.front = Some(reference); + } + + if faces.intersects(ash::vk::StencilFaceFlags::BACK) { + self.current_state.stencil_reference.back = Some(reference); + } + } + + /// Calls `vkCmdSetStencilTestEnableEXT` on the builder. + #[inline] + pub unsafe fn set_stencil_test_enable(&mut self, enable: bool) { + struct Cmd { + enable: bool, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_stencil_test_enable" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_stencil_test_enable(self.enable); + } + } + + self.commands.push(Box::new(Cmd { enable })); + self.current_state.stencil_test_enable = Some(enable); + } + + /// Calls `vkCmdSetStencilWriteMask` on the builder. + #[inline] + pub unsafe fn set_stencil_write_mask(&mut self, faces: StencilFaces, write_mask: u32) { + struct Cmd { + faces: StencilFaces, + write_mask: u32, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_stencil_write_mask" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_stencil_write_mask(self.faces, self.write_mask); + } + } + + self.commands.push(Box::new(Cmd { faces, write_mask })); + + let faces = ash::vk::StencilFaceFlags::from(faces); + + if faces.intersects(ash::vk::StencilFaceFlags::FRONT) { + self.current_state.stencil_write_mask.front = Some(write_mask); + } + + if faces.intersects(ash::vk::StencilFaceFlags::BACK) { + self.current_state.stencil_write_mask.back = Some(write_mask); + } + } + + /// Calls `vkCmdSetScissor` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_scissor( + &mut self, + first_scissor: u32, + scissors: impl IntoIterator<Item = Scissor>, + ) { + struct Cmd { + first_scissor: u32, + scissors: Mutex<SmallVec<[Scissor; 2]>>, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_scissor" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_scissor(self.first_scissor, self.scissors.lock().drain(..)); + } + } + + let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect(); + + for (num, scissor) in scissors.iter().enumerate() { + let num = num as u32 + first_scissor; + self.current_state.scissor.insert(num, *scissor); + } + + self.commands.push(Box::new(Cmd { + first_scissor, + scissors: Mutex::new(scissors), + })); + } + + /// Calls `vkCmdSetScissorWithCountEXT` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_scissor_with_count(&mut self, scissors: impl IntoIterator<Item = Scissor>) { + struct Cmd { + scissors: Mutex<SmallVec<[Scissor; 2]>>, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_scissor_with_count" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_scissor_with_count(self.scissors.lock().drain(..)); + } + } + + let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect(); + self.current_state.scissor_with_count = Some(scissors.clone()); + self.commands.push(Box::new(Cmd { + scissors: Mutex::new(scissors), + })); + } + + /// Calls `vkCmdSetViewport` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_viewport( + &mut self, + first_viewport: u32, + viewports: impl IntoIterator<Item = Viewport>, + ) { + struct Cmd { + first_viewport: u32, + viewports: Mutex<SmallVec<[Viewport; 2]>>, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_viewport" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_viewport(self.first_viewport, self.viewports.lock().drain(..)); + } + } + + let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect(); + + for (num, viewport) in viewports.iter().enumerate() { + let num = num as u32 + first_viewport; + self.current_state.viewport.insert(num, viewport.clone()); + } + + self.commands.push(Box::new(Cmd { + first_viewport, + viewports: Mutex::new(viewports), + })); + } + + /// Calls `vkCmdSetViewportWithCountEXT` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_viewport_with_count( + &mut self, + viewports: impl IntoIterator<Item = Viewport>, + ) { + struct Cmd { + viewports: Mutex<SmallVec<[Viewport; 2]>>, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "set_viewport_with_count" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.set_viewport_with_count(self.viewports.lock().drain(..)); + } + } + + let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect(); + self.current_state.viewport_with_count = Some(viewports.clone()); + self.commands.push(Box::new(Cmd { + viewports: Mutex::new(viewports), + })); + } +} + +impl UnsafeCommandBufferBuilder { + /// Calls `vkCmdSetBlendConstants` on the builder. + #[inline] + pub unsafe fn set_blend_constants(&mut self, constants: [f32; 4]) { + let fns = self.device.fns(); + (fns.v1_0.cmd_set_blend_constants)(self.handle, &constants); + } + + /// Calls `vkCmdSetColorWriteEnableEXT` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_color_write_enable(&mut self, enables: impl IntoIterator<Item = bool>) { + debug_assert!(self.device.enabled_extensions().ext_color_write_enable); + + let enables = enables + .into_iter() + .map(|v| v as ash::vk::Bool32) + .collect::<SmallVec<[_; 4]>>(); + if enables.is_empty() { + return; + } + + let fns = self.device.fns(); + (fns.ext_color_write_enable.cmd_set_color_write_enable_ext)( + self.handle, + enables.len() as u32, + enables.as_ptr(), + ); + } + + /// Calls `vkCmdSetCullModeEXT` on the builder. + #[inline] + pub unsafe fn set_cull_mode(&mut self, cull_mode: CullMode) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_cull_mode)(self.handle, cull_mode.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state.cmd_set_cull_mode_ext)(self.handle, cull_mode.into()); + } + } + + /// Calls `vkCmdSetDepthBias` on the builder. + #[inline] + pub unsafe fn set_depth_bias(&mut self, constant_factor: f32, clamp: f32, slope_factor: f32) { + let fns = self.device.fns(); + (fns.v1_0.cmd_set_depth_bias)(self.handle, constant_factor, clamp, slope_factor); + } + + /// Calls `vkCmdSetDepthBiasEnableEXT` on the builder. + #[inline] + pub unsafe fn set_depth_bias_enable(&mut self, enable: bool) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_depth_bias_enable)(self.handle, enable.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state2); + (fns.ext_extended_dynamic_state2 + .cmd_set_depth_bias_enable_ext)(self.handle, enable.into()); + } + } + + /// Calls `vkCmdSetDepthBounds` on the builder. + #[inline] + pub unsafe fn set_depth_bounds(&mut self, bounds: RangeInclusive<f32>) { + let fns = self.device.fns(); + (fns.v1_0.cmd_set_depth_bounds)(self.handle, *bounds.start(), *bounds.end()); + } + + /// Calls `vkCmdSetDepthBoundsTestEnableEXT` on the builder. + #[inline] + pub unsafe fn set_depth_bounds_test_enable(&mut self, enable: bool) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_depth_bounds_test_enable)(self.handle, enable.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state + .cmd_set_depth_bounds_test_enable_ext)(self.handle, enable.into()); + } + } + + /// Calls `vkCmdSetDepthCompareOpEXT` on the builder. + #[inline] + pub unsafe fn set_depth_compare_op(&mut self, compare_op: CompareOp) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_depth_compare_op)(self.handle, compare_op.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state.cmd_set_depth_compare_op_ext)( + self.handle, + compare_op.into(), + ); + } + } + + /// Calls `vkCmdSetDepthTestEnableEXT` on the builder. + #[inline] + pub unsafe fn set_depth_test_enable(&mut self, enable: bool) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_depth_test_enable)(self.handle, enable.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state.cmd_set_depth_test_enable_ext)( + self.handle, + enable.into(), + ); + } + } + + /// Calls `vkCmdSetDepthWriteEnableEXT` on the builder. + #[inline] + pub unsafe fn set_depth_write_enable(&mut self, enable: bool) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_depth_write_enable)(self.handle, enable.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state + .cmd_set_depth_write_enable_ext)(self.handle, enable.into()); + } + } + + /// Calls `vkCmdSetDiscardRectangleEXT` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_discard_rectangle( + &mut self, + first_rectangle: u32, + rectangles: impl IntoIterator<Item = Scissor>, + ) { + debug_assert!(self.device.enabled_extensions().ext_discard_rectangles); + + let rectangles = rectangles + .into_iter() + .map(|v| v.into()) + .collect::<SmallVec<[_; 2]>>(); + if rectangles.is_empty() { + return; + } + + debug_assert!( + first_rectangle + rectangles.len() as u32 + <= self + .device + .physical_device() + .properties() + .max_discard_rectangles + .unwrap() + ); + + let fns = self.device.fns(); + (fns.ext_discard_rectangles.cmd_set_discard_rectangle_ext)( + self.handle, + first_rectangle, + rectangles.len() as u32, + rectangles.as_ptr(), + ); + } + + /// Calls `vkCmdSetFrontFaceEXT` on the builder. + #[inline] + pub unsafe fn set_front_face(&mut self, face: FrontFace) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_front_face)(self.handle, face.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state.cmd_set_front_face_ext)(self.handle, face.into()); + } + } + + /// Calls `vkCmdSetLineStippleEXT` on the builder. + #[inline] + pub unsafe fn set_line_stipple(&mut self, factor: u32, pattern: u16) { + debug_assert!(self.device.enabled_extensions().ext_line_rasterization); + let fns = self.device.fns(); + (fns.ext_line_rasterization.cmd_set_line_stipple_ext)(self.handle, factor, pattern); + } + + /// Calls `vkCmdSetLineWidth` on the builder. + #[inline] + pub unsafe fn set_line_width(&mut self, line_width: f32) { + let fns = self.device.fns(); + (fns.v1_0.cmd_set_line_width)(self.handle, line_width); + } + + /// Calls `vkCmdSetLogicOpEXT` on the builder. + #[inline] + pub unsafe fn set_logic_op(&mut self, logic_op: LogicOp) { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state2); + debug_assert!( + self.device + .enabled_features() + .extended_dynamic_state2_logic_op + ); + let fns = self.device.fns(); + + (fns.ext_extended_dynamic_state2.cmd_set_logic_op_ext)(self.handle, logic_op.into()); + } + + /// Calls `vkCmdSetPatchControlPointsEXT` on the builder. + #[inline] + pub unsafe fn set_patch_control_points(&mut self, num: u32) { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state2); + let fns = self.device.fns(); + (fns.ext_extended_dynamic_state2 + .cmd_set_patch_control_points_ext)(self.handle, num); + } + + /// Calls `vkCmdSetPrimitiveRestartEnableEXT` on the builder. + #[inline] + pub unsafe fn set_primitive_restart_enable(&mut self, enable: bool) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_primitive_restart_enable)(self.handle, enable.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state2); + (fns.ext_extended_dynamic_state2 + .cmd_set_primitive_restart_enable_ext)(self.handle, enable.into()); + } + } + + /// Calls `vkCmdSetPrimitiveTopologyEXT` on the builder. + #[inline] + pub unsafe fn set_primitive_topology(&mut self, topology: PrimitiveTopology) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_primitive_topology)(self.handle, topology.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state + .cmd_set_primitive_topology_ext)(self.handle, topology.into()); + } + } + + /// Calls `vkCmdSetRasterizerDiscardEnableEXT` on the builder. + #[inline] + pub unsafe fn set_rasterizer_discard_enable(&mut self, enable: bool) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_rasterizer_discard_enable)(self.handle, enable.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state2); + (fns.ext_extended_dynamic_state2 + .cmd_set_rasterizer_discard_enable_ext)(self.handle, enable.into()); + } + } + + /// Calls `vkCmdSetStencilCompareMask` on the builder. + #[inline] + pub unsafe fn set_stencil_compare_mask(&mut self, face_mask: StencilFaces, compare_mask: u32) { + let fns = self.device.fns(); + (fns.v1_0.cmd_set_stencil_compare_mask)(self.handle, face_mask.into(), compare_mask); + } + + /// Calls `vkCmdSetStencilOpEXT` on the builder. + #[inline] + pub unsafe fn set_stencil_op( + &mut self, + face_mask: StencilFaces, + fail_op: StencilOp, + pass_op: StencilOp, + depth_fail_op: StencilOp, + compare_op: CompareOp, + ) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_stencil_op)( + self.handle, + face_mask.into(), + fail_op.into(), + pass_op.into(), + depth_fail_op.into(), + compare_op.into(), + ); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state.cmd_set_stencil_op_ext)( + self.handle, + face_mask.into(), + fail_op.into(), + pass_op.into(), + depth_fail_op.into(), + compare_op.into(), + ); + } + } + + /// Calls `vkCmdSetStencilReference` on the builder. + #[inline] + pub unsafe fn set_stencil_reference(&mut self, face_mask: StencilFaces, reference: u32) { + let fns = self.device.fns(); + (fns.v1_0.cmd_set_stencil_reference)(self.handle, face_mask.into(), reference); + } + + /// Calls `vkCmdSetStencilTestEnableEXT` on the builder. + #[inline] + pub unsafe fn set_stencil_test_enable(&mut self, enable: bool) { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_stencil_test_enable)(self.handle, enable.into()); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state + .cmd_set_stencil_test_enable_ext)(self.handle, enable.into()); + } + } + + /// Calls `vkCmdSetStencilWriteMask` on the builder. + #[inline] + pub unsafe fn set_stencil_write_mask(&mut self, face_mask: StencilFaces, write_mask: u32) { + let fns = self.device.fns(); + (fns.v1_0.cmd_set_stencil_write_mask)(self.handle, face_mask.into(), write_mask); + } + + /// Calls `vkCmdSetScissor` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_scissor( + &mut self, + first_scissor: u32, + scissors: impl IntoIterator<Item = Scissor>, + ) { + let scissors = scissors + .into_iter() + .map(ash::vk::Rect2D::from) + .collect::<SmallVec<[_; 2]>>(); + if scissors.is_empty() { + return; + } + + let fns = self.device.fns(); + (fns.v1_0.cmd_set_scissor)( + self.handle, + first_scissor, + scissors.len() as u32, + scissors.as_ptr(), + ); + } + + /// Calls `vkCmdSetScissorWithCountEXT` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_scissor_with_count(&mut self, scissors: impl IntoIterator<Item = Scissor>) { + let scissors = scissors + .into_iter() + .map(ash::vk::Rect2D::from) + .collect::<SmallVec<[_; 2]>>(); + if scissors.is_empty() { + return; + } + + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_scissor_with_count)( + self.handle, + scissors.len() as u32, + scissors.as_ptr(), + ); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state + .cmd_set_scissor_with_count_ext)( + self.handle, + scissors.len() as u32, + scissors.as_ptr(), + ); + } + } + + /// Calls `vkCmdSetViewport` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_viewport( + &mut self, + first_viewport: u32, + viewports: impl IntoIterator<Item = Viewport>, + ) { + let viewports = viewports + .into_iter() + .map(|v| v.into()) + .collect::<SmallVec<[_; 2]>>(); + if viewports.is_empty() { + return; + } + + let fns = self.device.fns(); + (fns.v1_0.cmd_set_viewport)( + self.handle, + first_viewport, + viewports.len() as u32, + viewports.as_ptr(), + ); + } + + /// Calls `vkCmdSetViewportWithCountEXT` on the builder. + /// + /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_viewport_with_count( + &mut self, + viewports: impl IntoIterator<Item = Viewport>, + ) { + let viewports = viewports + .into_iter() + .map(|v| v.into()) + .collect::<SmallVec<[_; 2]>>(); + if viewports.is_empty() { + return; + } + + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_viewport_with_count)( + self.handle, + viewports.len() as u32, + viewports.as_ptr(), + ); + } else { + debug_assert!(self.device.enabled_extensions().ext_extended_dynamic_state); + (fns.ext_extended_dynamic_state + .cmd_set_viewport_with_count_ext)( + self.handle, + viewports.len() as u32, + viewports.as_ptr(), + ); + } + } +} + +#[derive(Clone, Debug)] +#[allow(dead_code)] +pub(in super::super) enum SetDynamicStateError { + RequirementNotMet { + required_for: &'static str, + requires_one_of: RequiresOneOf, + }, + + /// The provided `factor` is not between 1 and 256 inclusive. + FactorOutOfRange, + + /// The [`max_discard_rectangles`](crate::device::Properties::max_discard_rectangles) + /// limit has been exceeded. + MaxDiscardRectanglesExceeded { provided: u32, max: u32 }, + + /// The [`max_tessellation_patch_size`](crate::device::Properties::max_tessellation_patch_size) + /// limit has been exceeded. + MaxTessellationPatchSizeExceeded { provided: u32, max: u32 }, + + /// The [`max_viewports`](crate::device::Properties::max_viewports) + /// limit has been exceeded. + MaxViewportsExceeded { provided: u32, max: u32 }, + + /// The queue family doesn't allow this operation. + NotSupportedByQueueFamily, + + /// The provided item count is different from the number of attachments in the color blend + /// state of the currently bound pipeline. + PipelineColorBlendAttachmentCountMismatch { + provided_count: u32, + required_count: u32, + }, + + /// The currently bound pipeline contains this state as internally fixed state, which cannot be + /// overridden with dynamic state. + PipelineHasFixedState, +} + +impl Error for SetDynamicStateError {} + +impl Display for SetDynamicStateError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + Self::RequirementNotMet { + required_for, + requires_one_of, + } => write!( + f, + "a requirement was not met for: {}; requires one of: {}", + required_for, requires_one_of, + ), + Self::FactorOutOfRange => write!( + f, + "the provided `factor` is not between 1 and 256 inclusive", + ), + Self::MaxDiscardRectanglesExceeded { .. } => { + write!(f, "the `max_discard_rectangles` limit has been exceeded") + } + Self::MaxTessellationPatchSizeExceeded { .. } => write!( + f, + "the `max_tessellation_patch_size` limit has been exceeded", + ), + Self::MaxViewportsExceeded { .. } => { + write!(f, "the `max_viewports` limit has been exceeded") + } + Self::NotSupportedByQueueFamily => { + write!(f, "the queue family doesn't allow this operation") + } + Self::PipelineColorBlendAttachmentCountMismatch { + provided_count, + required_count, + } => write!( + f, + "the provided item count ({}) is different from the number of attachments in the \ + color blend state of the currently bound pipeline ({})", + provided_count, required_count, + ), + Self::PipelineHasFixedState => write!( + f, + "the currently bound pipeline contains this state as internally fixed state, which \ + cannot be overridden with dynamic state", + ), + } + } +} + +impl From<RequirementNotMet> for SetDynamicStateError { + fn from(err: RequirementNotMet) -> Self { + Self::RequirementNotMet { + required_for: err.required_for, + requires_one_of: err.requires_one_of, + } + } +} |