aboutsummaryrefslogtreecommitdiff
path: root/src/command_buffer/commands/dynamic_state.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/command_buffer/commands/dynamic_state.rs')
-rw-r--r--src/command_buffer/commands/dynamic_state.rs3006
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,
+ }
+ }
+}