From d54886a47a9f0412b04d5006761f9252695f8a05 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Mon, 14 Oct 2024 10:14:50 +1100 Subject: [PATCH 1/9] support `khr_fragment_shading_rate` --- vulkano/src/command_buffer/auto/builder.rs | 4 +- .../command_buffer/commands/dynamic_state.rs | 69 ++++++ .../src/command_buffer/commands/pipeline.rs | 20 +- .../graphics/fragment_shading_rate.rs | 201 ++++++++++++++++++ vulkano/src/pipeline/graphics/mod.rs | 87 ++++++++ vulkano/src/pipeline/mod.rs | 9 +- 6 files changed, 385 insertions(+), 5 deletions(-) create mode 100644 vulkano/src/pipeline/graphics/fragment_shading_rate.rs diff --git a/vulkano/src/command_buffer/auto/builder.rs b/vulkano/src/command_buffer/auto/builder.rs index 4a47d3e94c..ab8f305319 100644 --- a/vulkano/src/command_buffer/auto/builder.rs +++ b/vulkano/src/command_buffer/auto/builder.rs @@ -19,6 +19,7 @@ use crate::{ graphics::{ color_blend::LogicOp, depth_stencil::{CompareOp, StencilOps}, + fragment_shading_rate::FragmentShadingRateState, input_assembly::PrimitiveTopology, rasterization::{ ConservativeRasterizationMode, CullMode, DepthBiasState, FrontFace, LineStipple, @@ -1212,6 +1213,7 @@ pub(in crate::command_buffer) struct CommandBufferBuilderState { pub(in crate::command_buffer) conservative_rasterization_mode: Option, pub(in crate::command_buffer) extra_primitive_overestimation_size: Option, + pub(in crate::command_buffer) fragment_shading_rate: Option, // Active queries pub(in crate::command_buffer) queries: HashMap, @@ -1243,7 +1245,7 @@ impl CommandBufferBuilderState { DynamicState::DepthWriteEnable => self.depth_write_enable = None, DynamicState::DiscardRectangle => self.discard_rectangle.clear(), // DynamicState::ExclusiveScissor => todo!(), - // DynamicState::FragmentShadingRate => todo!(), + DynamicState::FragmentShadingRate => self.fragment_shading_rate = None, DynamicState::FrontFace => self.front_face = None, DynamicState::LineStipple => self.line_stipple = None, DynamicState::LineWidth => self.line_width = None, diff --git a/vulkano/src/command_buffer/commands/dynamic_state.rs b/vulkano/src/command_buffer/commands/dynamic_state.rs index b01b350ad6..b4a69982a1 100644 --- a/vulkano/src/command_buffer/commands/dynamic_state.rs +++ b/vulkano/src/command_buffer/commands/dynamic_state.rs @@ -5,6 +5,7 @@ use crate::{ graphics::{ color_blend::LogicOp, depth_stencil::{CompareOp, StencilFaces, StencilOp, StencilOps}, + fragment_shading_rate::{FragmentShadingRateCombinerOp, FragmentShadingRateState}, input_assembly::PrimitiveTopology, rasterization::{ ConservativeRasterizationMode, CullMode, DepthBiasState, FrontFace, LineStipple, @@ -1280,6 +1281,49 @@ impl RecordingCommandBuffer { self } + + /// Sets the dynamic fragment shading rate for future draw calls. + #[inline] + pub fn set_fragment_shading_rate( + &mut self, + fragment_size: [u32; 2], + combiner_ops: [FragmentShadingRateCombinerOp; 2], + ) -> Result<&mut Self, Box> { + self.validate_set_fragment_shading_rate()?; + + unsafe { Ok(self.set_fragment_shading_rate_unchecked(fragment_size, combiner_ops)) } + } + + fn validate_set_fragment_shading_rate(&self) -> Result<(), Box> { + // self.inner.validate_set_conservative_rasterization_mode()?; + + self.validate_graphics_pipeline_fixed_state(DynamicState::FragmentShadingRate)?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_fragment_shading_rate_unchecked( + &mut self, + fragment_size: [u32; 2], + combiner_ops: [FragmentShadingRateCombinerOp; 2], + ) -> &mut Self { + self.builder_state.fragment_shading_rate = Some(FragmentShadingRateState { + fragment_size, + combiner_ops, + ..FragmentShadingRateState::default() + }); + + self.add_command( + "set_fragment_shading_rate", + Default::default(), + move |out: &mut RawRecordingCommandBuffer| { + out.set_fragment_shading_rate_unchecked(fragment_size, combiner_ops); + }, + ); + + self + } } impl RawRecordingCommandBuffer { @@ -3381,4 +3425,29 @@ impl RawRecordingCommandBuffer { self } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_fragment_shading_rate_unchecked( + &mut self, + fragment_size: [u32; 2], + combiner_ops: [FragmentShadingRateCombinerOp; 2], + ) -> &mut Self { + let fns = self.device().fns(); + + let fragment_size = ash::vk::Extent2D { + width: fragment_size[0], + height: fragment_size[1], + }; + let combiner_ops: [ash::vk::FragmentShadingRateCombinerOpKHR; 2] = + [combiner_ops[0].into(), combiner_ops[1].into()]; + + (fns.khr_fragment_shading_rate + .cmd_set_fragment_shading_rate_khr)( + self.handle(), + &fragment_size, + combiner_ops.as_ptr().cast(), + ); + + self + } } diff --git a/vulkano/src/command_buffer/commands/pipeline.rs b/vulkano/src/command_buffer/commands/pipeline.rs index 5ae954e0a8..a6b44e8968 100644 --- a/vulkano/src/command_buffer/commands/pipeline.rs +++ b/vulkano/src/command_buffer/commands/pipeline.rs @@ -2633,7 +2633,25 @@ impl RecordingCommandBuffer { } } // DynamicState::ExclusiveScissor => todo!(), - // DynamicState::FragmentShadingRate => todo!(), + DynamicState::FragmentShadingRate => { + if self.builder_state.fragment_shading_rate.is_none() { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ) + .into(), + vuids: vuids!( + vuid_type, + "VUID-vkCmdDrawIndexed-pipelineFragmentShadingRate-09238" + ), + ..Default::default() + })); + } + } DynamicState::FrontFace => { if self.builder_state.front_face.is_none() { return Err(Box::new(ValidationError { diff --git a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs new file mode 100644 index 0000000000..7369044347 --- /dev/null +++ b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs @@ -0,0 +1,201 @@ +use crate::{device::Device, macros::vulkan_enum, ValidationError}; +use ash::vk; + +/// The state in a graphics pipeline describing the fragment shading rate. +#[derive(Clone, Debug)] +pub struct FragmentShadingRateState { + /// The pipeline fragment shading rate. + /// + /// The default value is `[1, 1]`. + pub fragment_size: [u32; 2], + + /// Determines how the pipeline, primitive, and attachment shading rates are combined for + /// fragments generated. + /// + /// The default value is `[FragmentShadingRateCombinerOp::Keep; 2]`. + pub combiner_ops: [FragmentShadingRateCombinerOp; 2], + + pub _ne: crate::NonExhaustive, +} + +impl Default for FragmentShadingRateState { + #[inline] + fn default() -> Self { + Self { + fragment_size: [1, 1], + combiner_ops: [FragmentShadingRateCombinerOp::Keep; 2], + _ne: crate::NonExhaustive(()), + } + } +} + +impl FragmentShadingRateState { + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + fragment_size, + combiner_ops, + _ne: _, + } = self; + + let properties = device.physical_device().properties(); + let features = device.enabled_features(); + + if fragment_size[0] == 0 { + return Err(Box::new(ValidationError { + context: "fragment_size[0]".into(), + problem: "fragment_size[0] must be greater than or equal to 1".into(), + vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494"], + ..Default::default() + })); + } + + if fragment_size[1] == 0 { + return Err(Box::new(ValidationError { + context: "fragment_size[1]".into(), + problem: "fragment_size[1] must be greater than or equal to 1".into(), + vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495"], + ..Default::default() + })); + } + + if !fragment_size[0].is_power_of_two() { + return Err(Box::new(ValidationError { + context: "fragment_size[0]".into(), + problem: "fragment_size[0] must be a power of two".into(), + vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496"], + ..Default::default() + })); + } + + if !fragment_size[1].is_power_of_two() { + return Err(Box::new(ValidationError { + context: "fragment_size[1]".into(), + problem: "fragment_size[1] must be a power of two".into(), + vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497"], + ..Default::default() + })); + } + + if fragment_size[0] > 4 { + return Err(Box::new(ValidationError { + context: "fragment_size[0]".into(), + problem: "fragment_size[0] must be less than or equal to 4".into(), + vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04498"], + ..Default::default() + })); + } + + if fragment_size[1] > 4 { + return Err(Box::new(ValidationError { + context: "fragment_size[1]".into(), + problem: "fragment_size[1] must be less than or equal to 4".into(), + vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04499"], + ..Default::default() + })); + } + + if !features.pipeline_fragment_shading_rate { + return Err(Box::new(ValidationError { + context: "features.pipeline_fragment_shading_rate".into(), + problem: "the pipeline_fragment_shading_rate feature must be enabled".into(), + vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04500"], + ..Default::default() + })); + } + + combiner_ops[0].validate_device(device).map_err(|err| { + err.add_context("combiner_ops[0]") + .set_vuids(&["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06567"]) + })?; + combiner_ops[1].validate_device(device).map_err(|err| { + err.add_context("combiner_ops[1]") + .set_vuids(&["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06568"]) + })?; + + if !features.primitive_fragment_shading_rate + && combiner_ops[0] != FragmentShadingRateCombinerOp::Keep + { + return Err(Box::new(ValidationError { + context: "combiner_ops[0]".into(), + problem: "the primitive_fragment_shading_rate feature must be enabled if combiner_ops[0] is not Keep".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04501", + ], + ..Default::default() + })); + } + + if !features.attachment_fragment_shading_rate + && combiner_ops[1] != FragmentShadingRateCombinerOp::Keep + { + return Err(Box::new(ValidationError { + context: "combiner_ops[1]".into(), + problem: "the attachment_fragment_shading_rate feature must be enabled if combiner_ops[1] is not Keep".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04502", + ], + ..Default::default() + })); + } + + if !properties.fragment_shading_rate_non_trivial_combiner_ops.unwrap() && // TODO: Is this unwrap OK? + ( + !(combiner_ops[0] == FragmentShadingRateCombinerOp::Keep || combiner_ops[0] == FragmentShadingRateCombinerOp::Replace) || + !(combiner_ops[1] == FragmentShadingRateCombinerOp::Keep || combiner_ops[1] == FragmentShadingRateCombinerOp::Replace) + ) { + return Err(Box::new(ValidationError { + context: "combiner_ops[0]".into(), + problem: "the fragment_shading_rate_non_trivial_combiner_ops feature must be enabled if combiner_ops[0] or combiner_ops[1] is not Keep or Replace".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-fragmentShadingRateNonTrivialCombinerOps-04506", + ], + ..Default::default() + })); + } + + Ok(()) + } + + pub(crate) fn to_vk<'a>(&self) -> ash::vk::PipelineFragmentShadingRateStateCreateInfoKHR<'a> { + let fragment_size = vk::Extent2D { + width: self.fragment_size[0], + height: self.fragment_size[1], + }; + let combiner_ops: [ash::vk::FragmentShadingRateCombinerOpKHR; 2] = + [self.combiner_ops[0].into(), self.combiner_ops[1].into()]; + + ash::vk::PipelineFragmentShadingRateStateCreateInfoKHR::default() + .fragment_size(fragment_size) + .combiner_ops(combiner_ops) + } +} + +vulkan_enum! { + #[non_exhaustive] + + /// Control how fragment shading rates are combined. + FragmentShadingRateCombinerOp = FragmentShadingRateCombinerOpKHR(i32); + + /// Specifies a combiner operation of combine(Axy,Bxy) = Axy. + Keep = KEEP, + + /// Specifies a combiner operation of combine(Axy,Bxy) = Bxy. + Replace = REPLACE, + + /// Specifies a combiner operation of combine(Axy,Bxy) = min(Axy,Bxy). + Min = MIN, + + /// Specifies a combiner operation of combine(Axy,Bxy) = max(Axy,Bxy). + Max = MAX, + + /// Specifies a combiner operation of combine(Axy,Bxy) = Axy * Bxy. + /// + /// See the vulkan specification for more information on how this operation is performed if `fragmentShadingRateStrictMultiplyCombiner` is `false`. + Mul = MUL, +} + +impl Default for FragmentShadingRateCombinerOp { + fn default() -> Self { + Self::Keep + } +} diff --git a/vulkano/src/pipeline/graphics/mod.rs b/vulkano/src/pipeline/graphics/mod.rs index 7e6d327704..e3eb1c85e8 100644 --- a/vulkano/src/pipeline/graphics/mod.rs +++ b/vulkano/src/pipeline/graphics/mod.rs @@ -114,6 +114,7 @@ use crate::{ Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError, VulkanObject, }; use ahash::{HashMap, HashSet}; +use fragment_shading_rate::FragmentShadingRateState; use smallvec::SmallVec; use std::{ collections::hash_map::Entry, fmt::Debug, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc, @@ -122,6 +123,7 @@ use std::{ pub mod color_blend; pub mod depth_stencil; pub mod discard_rectangle; +pub mod fragment_shading_rate; pub mod input_assembly; pub mod multisample; pub mod rasterization; @@ -157,6 +159,7 @@ pub struct GraphicsPipeline { subpass: PipelineSubpassType, discard_rectangle_state: Option, + fragment_shading_rate_state: Option, descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>, num_used_descriptor_sets: u32, @@ -272,6 +275,8 @@ impl GraphicsPipeline { discard_rectangle_state, + fragment_shading_rate_state, + _ne: _, } = create_info; @@ -409,6 +414,10 @@ impl GraphicsPipeline { fixed_state.extend([DynamicState::DiscardRectangle]); } + if fragment_shading_rate_state.is_some() { + fixed_state.extend([DynamicState::FragmentShadingRate]); + } + fixed_state.retain(|state| !dynamic_state.contains(state)); Arc::new(Self { @@ -432,6 +441,7 @@ impl GraphicsPipeline { subpass: subpass.unwrap(), discard_rectangle_state, + fragment_shading_rate_state, descriptor_binding_requirements, num_used_descriptor_sets, @@ -531,6 +541,12 @@ impl GraphicsPipeline { self.discard_rectangle_state.as_ref() } + /// Returns the fragment shading rate state used to create this pipeline. + #[inline] + pub fn fragment_shading_rate_state(&self) -> Option<&FragmentShadingRateState> { + self.fragment_shading_rate_state.as_ref() + } + /// If the pipeline has a fragment shader, returns the fragment tests stages used. #[inline] pub fn fragment_tests_stages(&self) -> Option { @@ -723,6 +739,11 @@ pub struct GraphicsPipelineCreateInfo { /// The default value is `None`. pub discard_rectangle_state: Option, + /// The fragment shading rate state. + /// + /// The default value is `None`. + pub fragment_shading_rate_state: Option, + pub _ne: crate::NonExhaustive, } @@ -749,6 +770,8 @@ impl GraphicsPipelineCreateInfo { base_pipeline: None, discard_rectangle_state: None, + fragment_shading_rate_state: None, + _ne: crate::NonExhaustive(()), } } @@ -775,6 +798,8 @@ impl GraphicsPipelineCreateInfo { ref base_pipeline, ref discard_rectangle_state, + ref fragment_shading_rate_state, + _ne: _, } = self; @@ -1205,6 +1230,37 @@ impl GraphicsPipelineCreateInfo { _ => (), } + match ( + fragment_shading_rate_state.is_some(), + need_pre_rasterization_shader_state, + ) { + (true, false) => { + return Err(Box::new(ValidationError { + problem: "the pipeline is not being created with \ + pre-rasterization state, but \ + `fragment_shading_rate_state` is `Some`" + .into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04498", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04499", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04500", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06567", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06568", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04501", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04502", + "VUID-VkGraphicsPipelineCreateInfo-fragmentShadingRateNonTrivialCombinerOps-04506", + ], + ..Default::default() + })); + } + (false, true) => (), + _ => (), + } + /* Validate shader stages individually */ @@ -1485,6 +1541,23 @@ impl GraphicsPipelineCreateInfo { .map_err(|err| err.add_context("discard_rectangle_state"))?; } + if let Some(fragment_shading_rate_state) = fragment_shading_rate_state { + if !device.enabled_extensions().khr_fragment_shading_rate { + return Err(Box::new(ValidationError { + context: "fragment_shading_rate_state".into(), + problem: "is `Some`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "khr_fragment_shading_rate", + )])]), + ..Default::default() + })); + } + + fragment_shading_rate_state + .validate(device) + .map_err(|err| err.add_context("fragment_shading_rate_state"))?; + } + for dynamic_state in dynamic_state.iter().copied() { dynamic_state.validate_device(device).map_err(|err| { err.add_context("dynamic_state") @@ -2287,6 +2360,8 @@ impl GraphicsPipelineCreateInfo { ref base_pipeline, discard_rectangle_state: _, + fragment_shading_rate_state: _, + _ne: _, } = self; let (render_pass_vk, subpass_vk) = match subpass { @@ -2360,12 +2435,17 @@ impl GraphicsPipelineCreateInfo { let GraphicsPipelineCreateInfoExtensionsVk { discard_rectangle_state_vk, rendering_vk, + fragment_shading_rate_vk, } = extensions_vk; if let Some(next) = discard_rectangle_state_vk { val_vk = val_vk.push_next(next); } + if let Some(next) = fragment_shading_rate_vk { + val_vk = val_vk.push_next(next); + } + if let Some(next) = rendering_vk { val_vk = val_vk.push_next(next); } @@ -2392,10 +2472,15 @@ impl GraphicsPipelineCreateInfo { .as_ref() .zip(rendering_fields1_vk.as_ref()) .map(|(subpass, fields1_vk)| subpass.to_vk_rendering(fields1_vk)); + let fragment_shading_rate_vk = self + .fragment_shading_rate_state + .as_ref() + .map(|fragment_shading_rate_state| fragment_shading_rate_state.to_vk()); GraphicsPipelineCreateInfoExtensionsVk { discard_rectangle_state_vk, rendering_vk, + fragment_shading_rate_vk, } } @@ -2608,6 +2693,8 @@ pub(crate) struct GraphicsPipelineCreateInfoExtensionsVk<'a> { pub(crate) discard_rectangle_state_vk: Option>, pub(crate) rendering_vk: Option>, + pub(crate) fragment_shading_rate_vk: + Option>, } pub(crate) struct GraphicsPipelineCreateInfoFields1Vk<'a> { diff --git a/vulkano/src/pipeline/mod.rs b/vulkano/src/pipeline/mod.rs index 1e094b6d24..ec7a3b5a95 100644 --- a/vulkano/src/pipeline/mod.rs +++ b/vulkano/src/pipeline/mod.rs @@ -588,12 +588,15 @@ vulkan_enum! { RequiresAllOf([DeviceExtension(nv_scissor_exclusive)]), ]), */ - /* TODO: enable - // TODO: document + /// The value of + /// [`FragmentShadingRateState`](crate::pipeline::graphics::fragment_shading_rate::FragmentShadingRateState). + /// + /// Set with + /// [`set_fragment_shading_rate`](crate::command_buffer::RecordingCommandBuffer::set_fragment_shading_rate). FragmentShadingRate = FRAGMENT_SHADING_RATE_KHR RequiresOneOf([ RequiresAllOf([DeviceExtension(khr_fragment_shading_rate)]), - ]), */ + ]), /// The value of /// [`RasterizationState::line_stipple`](crate::pipeline::graphics::rasterization::RasterizationState::line_stipple). From 7ce88e8d043b6b44c1903715b6a6c9c704901b1b Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Fri, 18 Oct 2024 21:09:51 +1100 Subject: [PATCH 2/9] Add taskgraph `set_fragment_shading_rate` --- .../command_buffer/commands/dynamic_state.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs b/vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs index e3d9168fc9..8564e2a44c 100644 --- a/vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs +++ b/vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs @@ -7,6 +7,7 @@ use vulkano::{ pipeline::graphics::{ color_blend::LogicOp, depth_stencil::{CompareOp, StencilFaces, StencilOp}, + fragment_shading_rate::FragmentShadingRateCombinerOp, input_assembly::PrimitiveTopology, rasterization::{ConservativeRasterizationMode, CullMode, FrontFace}, vertex_input::{ @@ -772,4 +773,35 @@ impl RecordingCommandBuffer<'_> { self } + + /// Sets the dynamic fragment shading rate for future draw calls. + #[inline] + pub fn set_fragment_shading_rate( + &mut self, + fragment_size: [u32; 2], + combiner_ops: [FragmentShadingRateCombinerOp; 2], + ) -> Result<&mut Self> { + unsafe { Ok(self.set_fragment_shading_rate_unchecked(fragment_size, combiner_ops)) } + } + + pub unsafe fn set_fragment_shading_rate_unchecked( + &mut self, + fragment_size: [u32; 2], + combiner_ops: [FragmentShadingRateCombinerOp; 2], + ) -> &mut Self { + let fns = self.device().fns(); + let fragment_size = vk::Extent2D { + width: fragment_size[0], + height: fragment_size[1], + }; + let combiner_ops = [combiner_ops[0].into(), combiner_ops[1].into()]; + unsafe { + (fns.khr_fragment_shading_rate + .cmd_set_fragment_shading_rate_khr)( + self.handle(), &fragment_size, &combiner_ops + ) + } + + self + } } From 1e44218fcc938d3539158638390f261dda48ab1e Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Fri, 18 Oct 2024 21:42:23 +1100 Subject: [PATCH 3/9] Add `set_fragment_shading_rate` validation --- .../command_buffer/commands/dynamic_state.rs | 121 +++++++++++++++++- 1 file changed, 118 insertions(+), 3 deletions(-) diff --git a/vulkano/src/command_buffer/commands/dynamic_state.rs b/vulkano/src/command_buffer/commands/dynamic_state.rs index b4a69982a1..5b618acaee 100644 --- a/vulkano/src/command_buffer/commands/dynamic_state.rs +++ b/vulkano/src/command_buffer/commands/dynamic_state.rs @@ -1289,13 +1289,18 @@ impl RecordingCommandBuffer { fragment_size: [u32; 2], combiner_ops: [FragmentShadingRateCombinerOp; 2], ) -> Result<&mut Self, Box> { - self.validate_set_fragment_shading_rate()?; + self.validate_set_fragment_shading_rate(fragment_size, combiner_ops)?; unsafe { Ok(self.set_fragment_shading_rate_unchecked(fragment_size, combiner_ops)) } } - fn validate_set_fragment_shading_rate(&self) -> Result<(), Box> { - // self.inner.validate_set_conservative_rasterization_mode()?; + fn validate_set_fragment_shading_rate( + &self, + fragment_size: [u32; 2], + combiner_ops: [FragmentShadingRateCombinerOp; 2], + ) -> Result<(), Box> { + self.inner + .validate_set_fragment_shading_rate(fragment_size, combiner_ops)?; self.validate_graphics_pipeline_fixed_state(DynamicState::FragmentShadingRate)?; @@ -3426,6 +3431,116 @@ impl RawRecordingCommandBuffer { self } + fn validate_set_fragment_shading_rate( + &self, + fragment_size: [u32; 2], + combiner_ops: [FragmentShadingRateCombinerOp; 2], + ) -> Result<(), Box> { + let device = self.device(); + let properties = device.physical_device().properties(); + let features = device.enabled_features(); + + match fragment_size[0] { + 1 | 2 | 4 => {} + _ => { + return Err(Box::new(ValidationError { + context: "fragment_size[0]".into(), + problem: "fragment_size[0] must be 1, 2, or 4".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04498", + ], + ..Default::default() + })); + } + } + + match fragment_size[1] { + 1 | 2 | 4 => {} + _ => { + return Err(Box::new(ValidationError { + context: "fragment_size[1]".into(), + problem: "fragment_size[1] must be 1, 2, or 4".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04499", + ], + ..Default::default() + })); + } + } + + if !(features.pipeline_fragment_shading_rate) { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature( + "pipeline_fragment_shading_rate", + )])]), + vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04500"], + ..Default::default() + })); + } + + combiner_ops[0].validate_device(device).map_err(|err| { + err.add_context("combiner_ops[0]") + .set_vuids(&["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06567"]) + })?; + + combiner_ops[1].validate_device(device).map_err(|err| { + err.add_context("combiner_ops[1]") + .set_vuids(&["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06568"]) + })?; + + if !features.primitive_fragment_shading_rate + && combiner_ops[0] != FragmentShadingRateCombinerOp::Keep + { + return Err(Box::new(ValidationError { + context: "combiner_ops[0]".into(), + problem: "the primitive_fragment_shading_rate feature must be enabled if combiner_ops[0] is not Keep".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04501", + ], + ..Default::default() + })); + } + + if !features.attachment_fragment_shading_rate + && combiner_ops[1] != FragmentShadingRateCombinerOp::Keep + { + return Err(Box::new(ValidationError { + context: "combiner_ops[1]".into(), + problem: "the attachment_fragment_shading_rate feature must be enabled if combiner_ops[1] is not Keep".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04502", + ], + ..Default::default() + })); + } + + if let Some(fragment_shading_rate_non_trivial_combiner_ops) = + properties.fragment_shading_rate_non_trivial_combiner_ops + { + if !fragment_shading_rate_non_trivial_combiner_ops + && (!(combiner_ops[0] == FragmentShadingRateCombinerOp::Keep + || combiner_ops[0] == FragmentShadingRateCombinerOp::Replace) + || !(combiner_ops[1] == FragmentShadingRateCombinerOp::Keep + || combiner_ops[1] == FragmentShadingRateCombinerOp::Replace)) + { + return Err(Box::new(ValidationError { + context: "combiner_ops[0]".into(), + problem: "the fragment_shading_rate_non_trivial_combiner_ops feature must be enabled if combiner_ops[0] or combiner_ops[1] is not Keep or Replace".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-fragmentShadingRateNonTrivialCombinerOps-04506", + ], + ..Default::default() + })); + } + } + + Ok(()) + } + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn set_fragment_shading_rate_unchecked( &mut self, From f42767b0544443a97af4e30c8cb83ebcdaee8c0f Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Fri, 18 Oct 2024 21:43:05 +1100 Subject: [PATCH 4/9] Cleanup `FragmentShadingRateState` validation --- .../graphics/fragment_shading_rate.rs | 96 ++++++++----------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs index 7369044347..751faa096f 100644 --- a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs +++ b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs @@ -40,58 +40,36 @@ impl FragmentShadingRateState { let properties = device.physical_device().properties(); let features = device.enabled_features(); - if fragment_size[0] == 0 { - return Err(Box::new(ValidationError { - context: "fragment_size[0]".into(), - problem: "fragment_size[0] must be greater than or equal to 1".into(), - vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494"], - ..Default::default() - })); + match fragment_size[0] { + 1 | 2 | 4 => {} + _ => { + return Err(Box::new(ValidationError { + context: "fragment_size[0]".into(), + problem: "fragment_size[0] must be 1, 2, or 4".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04498", + ], + ..Default::default() + })); + } } - if fragment_size[1] == 0 { - return Err(Box::new(ValidationError { - context: "fragment_size[1]".into(), - problem: "fragment_size[1] must be greater than or equal to 1".into(), - vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495"], - ..Default::default() - })); - } - - if !fragment_size[0].is_power_of_two() { - return Err(Box::new(ValidationError { - context: "fragment_size[0]".into(), - problem: "fragment_size[0] must be a power of two".into(), - vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496"], - ..Default::default() - })); - } - - if !fragment_size[1].is_power_of_two() { - return Err(Box::new(ValidationError { - context: "fragment_size[1]".into(), - problem: "fragment_size[1] must be a power of two".into(), - vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497"], - ..Default::default() - })); - } - - if fragment_size[0] > 4 { - return Err(Box::new(ValidationError { - context: "fragment_size[0]".into(), - problem: "fragment_size[0] must be less than or equal to 4".into(), - vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04498"], - ..Default::default() - })); - } - - if fragment_size[1] > 4 { - return Err(Box::new(ValidationError { - context: "fragment_size[1]".into(), - problem: "fragment_size[1] must be less than or equal to 4".into(), - vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04499"], - ..Default::default() - })); + match fragment_size[1] { + 1 | 2 | 4 => {} + _ => { + return Err(Box::new(ValidationError { + context: "fragment_size[1]".into(), + problem: "fragment_size[1] must be 1, 2, or 4".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04499", + ], + ..Default::default() + })); + } } if !features.pipeline_fragment_shading_rate { @@ -107,6 +85,7 @@ impl FragmentShadingRateState { err.add_context("combiner_ops[0]") .set_vuids(&["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06567"]) })?; + combiner_ops[1].validate_device(device).map_err(|err| { err.add_context("combiner_ops[1]") .set_vuids(&["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06568"]) @@ -138,12 +117,16 @@ impl FragmentShadingRateState { })); } - if !properties.fragment_shading_rate_non_trivial_combiner_ops.unwrap() && // TODO: Is this unwrap OK? - ( - !(combiner_ops[0] == FragmentShadingRateCombinerOp::Keep || combiner_ops[0] == FragmentShadingRateCombinerOp::Replace) || - !(combiner_ops[1] == FragmentShadingRateCombinerOp::Keep || combiner_ops[1] == FragmentShadingRateCombinerOp::Replace) - ) { - return Err(Box::new(ValidationError { + if let Some(fragment_shading_rate_non_trivial_combiner_ops) = + properties.fragment_shading_rate_non_trivial_combiner_ops + { + if !fragment_shading_rate_non_trivial_combiner_ops + && (!(combiner_ops[0] == FragmentShadingRateCombinerOp::Keep + || combiner_ops[0] == FragmentShadingRateCombinerOp::Replace) + || !(combiner_ops[1] == FragmentShadingRateCombinerOp::Keep + || combiner_ops[1] == FragmentShadingRateCombinerOp::Replace)) + { + return Err(Box::new(ValidationError { context: "combiner_ops[0]".into(), problem: "the fragment_shading_rate_non_trivial_combiner_ops feature must be enabled if combiner_ops[0] or combiner_ops[1] is not Keep or Replace".into(), vuids: &[ @@ -151,6 +134,7 @@ impl FragmentShadingRateState { ], ..Default::default() })); + } } Ok(()) From fad81f0f0869e0c1d3bceb0f1dbdefd8be02fe63 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Fri, 18 Oct 2024 21:54:25 +1100 Subject: [PATCH 5/9] Consolidate fragment_shading_rate validation --- .../command_buffer/commands/dynamic_state.rs | 105 +----------------- .../graphics/fragment_shading_rate.rs | 61 +++++----- 2 files changed, 34 insertions(+), 132 deletions(-) diff --git a/vulkano/src/command_buffer/commands/dynamic_state.rs b/vulkano/src/command_buffer/commands/dynamic_state.rs index 5b618acaee..db4ec835d4 100644 --- a/vulkano/src/command_buffer/commands/dynamic_state.rs +++ b/vulkano/src/command_buffer/commands/dynamic_state.rs @@ -3436,107 +3436,12 @@ impl RawRecordingCommandBuffer { fragment_size: [u32; 2], combiner_ops: [FragmentShadingRateCombinerOp; 2], ) -> Result<(), Box> { - let device = self.device(); - let properties = device.physical_device().properties(); - let features = device.enabled_features(); - - match fragment_size[0] { - 1 | 2 | 4 => {} - _ => { - return Err(Box::new(ValidationError { - context: "fragment_size[0]".into(), - problem: "fragment_size[0] must be 1, 2, or 4".into(), - vuids: &[ - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494", - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496", - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04498", - ], - ..Default::default() - })); - } - } - - match fragment_size[1] { - 1 | 2 | 4 => {} - _ => { - return Err(Box::new(ValidationError { - context: "fragment_size[1]".into(), - problem: "fragment_size[1] must be 1, 2, or 4".into(), - vuids: &[ - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495", - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497", - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04499", - ], - ..Default::default() - })); - } - } - - if !(features.pipeline_fragment_shading_rate) { - return Err(Box::new(ValidationError { - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature( - "pipeline_fragment_shading_rate", - )])]), - vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04500"], - ..Default::default() - })); - } - - combiner_ops[0].validate_device(device).map_err(|err| { - err.add_context("combiner_ops[0]") - .set_vuids(&["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06567"]) - })?; - - combiner_ops[1].validate_device(device).map_err(|err| { - err.add_context("combiner_ops[1]") - .set_vuids(&["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06568"]) - })?; - - if !features.primitive_fragment_shading_rate - && combiner_ops[0] != FragmentShadingRateCombinerOp::Keep - { - return Err(Box::new(ValidationError { - context: "combiner_ops[0]".into(), - problem: "the primitive_fragment_shading_rate feature must be enabled if combiner_ops[0] is not Keep".into(), - vuids: &[ - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04501", - ], - ..Default::default() - })); - } - - if !features.attachment_fragment_shading_rate - && combiner_ops[1] != FragmentShadingRateCombinerOp::Keep - { - return Err(Box::new(ValidationError { - context: "combiner_ops[1]".into(), - problem: "the attachment_fragment_shading_rate feature must be enabled if combiner_ops[1] is not Keep".into(), - vuids: &[ - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04502", - ], - ..Default::default() - })); - } - - if let Some(fragment_shading_rate_non_trivial_combiner_ops) = - properties.fragment_shading_rate_non_trivial_combiner_ops - { - if !fragment_shading_rate_non_trivial_combiner_ops - && (!(combiner_ops[0] == FragmentShadingRateCombinerOp::Keep - || combiner_ops[0] == FragmentShadingRateCombinerOp::Replace) - || !(combiner_ops[1] == FragmentShadingRateCombinerOp::Keep - || combiner_ops[1] == FragmentShadingRateCombinerOp::Replace)) - { - return Err(Box::new(ValidationError { - context: "combiner_ops[0]".into(), - problem: "the fragment_shading_rate_non_trivial_combiner_ops feature must be enabled if combiner_ops[0] or combiner_ops[1] is not Keep or Replace".into(), - vuids: &[ - "VUID-VkGraphicsPipelineCreateInfo-fragmentShadingRateNonTrivialCombinerOps-04506", - ], - ..Default::default() - })); - } + FragmentShadingRateState { + fragment_size, + combiner_ops, + ..FragmentShadingRateState::default() } + .validate(self.device())?; Ok(()) } diff --git a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs index 751faa096f..37cc3323fe 100644 --- a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs +++ b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs @@ -40,36 +40,30 @@ impl FragmentShadingRateState { let properties = device.physical_device().properties(); let features = device.enabled_features(); - match fragment_size[0] { - 1 | 2 | 4 => {} - _ => { - return Err(Box::new(ValidationError { - context: "fragment_size[0]".into(), - problem: "fragment_size[0] must be 1, 2, or 4".into(), - vuids: &[ - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494", - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496", - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04498", - ], - ..Default::default() - })); - } + if !matches!(fragment_size[0], 1 | 2 | 4) { + return Err(Box::new(ValidationError { + context: "fragment_size[0]".into(), + problem: "fragment_size[0] must be 1, 2, or 4".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04498", + ], + ..Default::default() + })); } - match fragment_size[1] { - 1 | 2 | 4 => {} - _ => { - return Err(Box::new(ValidationError { - context: "fragment_size[1]".into(), - problem: "fragment_size[1] must be 1, 2, or 4".into(), - vuids: &[ - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495", - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497", - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04499", - ], - ..Default::default() - })); - } + if !matches!(fragment_size[1], 1 | 2 | 4) { + return Err(Box::new(ValidationError { + context: "fragment_size[1]".into(), + problem: "fragment_size[1] must be 1, 2, or 4".into(), + vuids: &[ + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497", + "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04499", + ], + ..Default::default() + })); } if !features.pipeline_fragment_shading_rate { @@ -121,10 +115,13 @@ impl FragmentShadingRateState { properties.fragment_shading_rate_non_trivial_combiner_ops { if !fragment_shading_rate_non_trivial_combiner_ops - && (!(combiner_ops[0] == FragmentShadingRateCombinerOp::Keep - || combiner_ops[0] == FragmentShadingRateCombinerOp::Replace) - || !(combiner_ops[1] == FragmentShadingRateCombinerOp::Keep - || combiner_ops[1] == FragmentShadingRateCombinerOp::Replace)) + && (!matches!( + combiner_ops[0], + FragmentShadingRateCombinerOp::Keep | FragmentShadingRateCombinerOp::Replace + ) || !matches!( + combiner_ops[1], + FragmentShadingRateCombinerOp::Keep | FragmentShadingRateCombinerOp::Replace + )) { return Err(Box::new(ValidationError { context: "combiner_ops[0]".into(), From 6dd8caae3f68f77b7a603baeb1128e90da42bce8 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Sat, 19 Oct 2024 08:53:31 +1100 Subject: [PATCH 6/9] Add docs for `fragment_shading_rate` --- .../src/pipeline/graphics/fragment_shading_rate.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs index 37cc3323fe..eed9a8e598 100644 --- a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs +++ b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs @@ -1,3 +1,17 @@ +//! Fragment shading rate introduces the ability to change the rate at which fragments are shaded. +//! +//! This feature is part of the `khr_fragment_shading_rate` extension (see [VK_KHR_fragment_shading_rate](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_fragment_shading_rate.html)). +//! +//! The fragment shading rate can be controlled per-draw, per-primitive, and per-region: +//! - Per-draw shading rate requires the `pipeline_fragment_shading_rate` feature, and the rate can +//! be set in a graphics pipeline or dynamically via `set_fragment_shading_rate`. +//! - Per-primitive shading rate requires the `primitive_fragment_shading_rate` feature, and is set +//! in the last active pre-rasterization shader stage. +//! - Per-region shading rate requires the `attachment_fragment_shading_rate` feature and an +//! additional specialised fragment shading rate image attachment. +//! +//! `vulkano` does not currently support per-region shading state. + use crate::{device::Device, macros::vulkan_enum, ValidationError}; use ash::vk; From f2c695dadb11d7fc41a029e9e66062df77cf6b3e Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Sat, 19 Oct 2024 15:07:03 +1100 Subject: [PATCH 7/9] Update vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com> --- vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs b/vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs index 8564e2a44c..79ae0b82a3 100644 --- a/vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs +++ b/vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs @@ -775,8 +775,7 @@ impl RecordingCommandBuffer<'_> { } /// Sets the dynamic fragment shading rate for future draw calls. - #[inline] - pub fn set_fragment_shading_rate( + pub unsafe fn set_fragment_shading_rate( &mut self, fragment_size: [u32; 2], combiner_ops: [FragmentShadingRateCombinerOp; 2], From 67f220d015c0a684e40d78951e186c6e913cb6fb Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Sat, 19 Oct 2024 15:26:49 +1100 Subject: [PATCH 8/9] Format fragment_shading_rate validation messages --- .../graphics/fragment_shading_rate.rs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs index eed9a8e598..43bd9914ea 100644 --- a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs +++ b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs @@ -1,6 +1,7 @@ //! Fragment shading rate introduces the ability to change the rate at which fragments are shaded. //! -//! This feature is part of the `khr_fragment_shading_rate` extension (see [VK_KHR_fragment_shading_rate](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_fragment_shading_rate.html)). +//! This feature is part of the `khr_fragment_shading_rate` extension. +//! See [VK_KHR_fragment_shading_rate](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_fragment_shading_rate.html)). //! //! The fragment shading rate can be controlled per-draw, per-primitive, and per-region: //! - Per-draw shading rate requires the `pipeline_fragment_shading_rate` feature, and the rate can @@ -57,7 +58,7 @@ impl FragmentShadingRateState { if !matches!(fragment_size[0], 1 | 2 | 4) { return Err(Box::new(ValidationError { context: "fragment_size[0]".into(), - problem: "fragment_size[0] must be 1, 2, or 4".into(), + problem: "`fragment_size[0]` must be 1, 2, or 4".into(), vuids: &[ "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494", "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496", @@ -70,7 +71,7 @@ impl FragmentShadingRateState { if !matches!(fragment_size[1], 1 | 2 | 4) { return Err(Box::new(ValidationError { context: "fragment_size[1]".into(), - problem: "fragment_size[1] must be 1, 2, or 4".into(), + problem: "`fragment_size[1]` must be 1, 2, or 4".into(), vuids: &[ "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495", "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497", @@ -83,7 +84,7 @@ impl FragmentShadingRateState { if !features.pipeline_fragment_shading_rate { return Err(Box::new(ValidationError { context: "features.pipeline_fragment_shading_rate".into(), - problem: "the pipeline_fragment_shading_rate feature must be enabled".into(), + problem: "the `pipeline_fragment_shading_rate` feature must be enabled".into(), vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04500"], ..Default::default() })); @@ -104,10 +105,10 @@ impl FragmentShadingRateState { { return Err(Box::new(ValidationError { context: "combiner_ops[0]".into(), - problem: "the primitive_fragment_shading_rate feature must be enabled if combiner_ops[0] is not Keep".into(), - vuids: &[ - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04501", - ], + problem: "the `primitive_fragment_shading_rate` feature must be enabled if \ + `combiner_ops[0]` is not `FragmentShadingRateCombinerOp::Keep`" + .into(), + vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04501"], ..Default::default() })); } @@ -117,10 +118,10 @@ impl FragmentShadingRateState { { return Err(Box::new(ValidationError { context: "combiner_ops[1]".into(), - problem: "the attachment_fragment_shading_rate feature must be enabled if combiner_ops[1] is not Keep".into(), - vuids: &[ - "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04502", - ], + problem: "the `attachment_fragment_shading_rate` feature must be enabled if \ + `combiner_ops[1]` is not `FragmentShadingRateCombinerOp::Keep`" + .into(), + vuids: &["VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04502"], ..Default::default() })); } @@ -139,7 +140,11 @@ impl FragmentShadingRateState { { return Err(Box::new(ValidationError { context: "combiner_ops[0]".into(), - problem: "the fragment_shading_rate_non_trivial_combiner_ops feature must be enabled if combiner_ops[0] or combiner_ops[1] is not Keep or Replace".into(), + problem: "the `fragment_shading_rate_non_trivial_combiner_ops` feature must be \ + enabled if `combiner_ops[0]` or `combiner_ops[1]` is not \ + `FragmentShadingRateCombinerOp::Keep` or \ + `FragmentShadingRateCombinerOp::Replace`" + .into(), vuids: &[ "VUID-VkGraphicsPipelineCreateInfo-fragmentShadingRateNonTrivialCombinerOps-04506", ], From 301dc9db209759e5b98d9f251e2f3262147cfb2a Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Sat, 19 Oct 2024 16:21:46 +1100 Subject: [PATCH 9/9] Apply suggestions from code review Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com> --- .../graphics/fragment_shading_rate.rs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs index 43bd9914ea..8ac8208917 100644 --- a/vulkano/src/pipeline/graphics/fragment_shading_rate.rs +++ b/vulkano/src/pipeline/graphics/fragment_shading_rate.rs @@ -1,17 +1,23 @@ //! Fragment shading rate introduces the ability to change the rate at which fragments are shaded. //! -//! This feature is part of the `khr_fragment_shading_rate` extension. -//! See [VK_KHR_fragment_shading_rate](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_fragment_shading_rate.html)). +//! This feature is part of the [`khr_fragment_shading_rate`] device extension. //! //! The fragment shading rate can be controlled per-draw, per-primitive, and per-region: -//! - Per-draw shading rate requires the `pipeline_fragment_shading_rate` feature, and the rate can -//! be set in a graphics pipeline or dynamically via `set_fragment_shading_rate`. -//! - Per-primitive shading rate requires the `primitive_fragment_shading_rate` feature, and is set -//! in the last active pre-rasterization shader stage. -//! - Per-region shading rate requires the `attachment_fragment_shading_rate` feature and an -//! additional specialised fragment shading rate image attachment. +//! - Per-draw shading rate requires the [`pipeline_fragment_shading_rate`] feature to be enabled +//! on the device, and the rate can be set in a graphics pipeline or dynamically via +//! [`set_fragment_shading_rate`]. +//! - Per-primitive shading rate requires the [`primitive_fragment_shading_rate`] feature to be +//! enabled on the device, and is set in the last active pre-rasterization shader stage. +//! - Per-region shading rate requires the [`attachment_fragment_shading_rate`] feature to be +//! enabled on the device and an additional specialised fragment shading rate image attachment. //! //! `vulkano` does not currently support per-region shading state. +//! +//! [`khr_fragment_shading_rate`]: crate::device::DeviceExtensions::khr_fragment_shading_rate +//! [`pipeline_fragment_shading_rate`]: crate::device::DeviceFeatures::pipeline_fragment_shading_rate +//! [`set_fragment_shading_rate`]: crate::command_buffer::AutoCommandBufferBuilder::set_fragment_shading_rate +//! [`primitive_fragment_shading_rate`]: crate::device::DeviceFeatures::primitive_fragment_shading_rate +//! [`attachment_fragment_shading_rate`]: crate::device::DeviceFeatures::attachment_fragment_shading_rate use crate::{device::Device, macros::vulkan_enum, ValidationError}; use ash::vk; @@ -195,6 +201,7 @@ vulkan_enum! { } impl Default for FragmentShadingRateCombinerOp { + #[inline] fn default() -> Self { Self::Keep }