From 4793b64d5803685de1c9353daedefe109e8d7755 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Fri, 7 Mar 2025 22:18:37 -0800 Subject: [PATCH] vulkan: Start on dynamic_state_commands system --- wgpu-hal/src/vulkan/adapter.rs | 107 +++++++++++- wgpu-hal/src/vulkan/command.rs | 135 ++++++++++++++- wgpu-hal/src/vulkan/conv.rs | 17 +- wgpu-hal/src/vulkan/device.rs | 307 +++++++++++++++++++++++++++------ wgpu-hal/src/vulkan/mod.rs | 44 +++++ 5 files changed, 545 insertions(+), 65 deletions(-) diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 6d0a039d72..04e726b55d 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -3,6 +3,8 @@ use std::{borrow::ToOwned as _, collections::BTreeMap, ffi::CStr, sync::Arc, vec use ash::{amd, ext, google, khr, vk}; use parking_lot::Mutex; +use crate::vulkan::DynamicStateFlags; + use super::conv; fn depth_stencil_required_flags() -> vk::FormatFeatureFlags { @@ -120,11 +122,17 @@ pub struct PhysicalDeviceFeatures { /// Features provided by `VK_EXT_subgroup_size_control`, promoted to Vulkan 1.3. subgroup_size_control: Option>, - /// Features proved by `VK_KHR_maintenance4`, needed for mesh shaders + /// Features provided by `VK_KHR_maintenance4`, needed for mesh shaders maintenance4: Option>, - /// Features proved by `VK_EXT_mesh_shader` + /// Features provided by `VK_EXT_mesh_shader` mesh_shader: Option>, + + /// Features provided by `VK_EXT_extended_dynamic_state` + extended_dynamic_state: Option>, + + /// Features provided by `VK_EXT_extended_dynamic_state3` + extended_dynamic_state3: Option>, } impl PhysicalDeviceFeatures { @@ -192,6 +200,9 @@ impl PhysicalDeviceFeatures { if let Some(ref mut feature) = self.mesh_shader { info = info.push_next(feature); } + if let Some(ref mut feature) = self.extended_dynamic_state { + info = info.push_next(feature); + } info } @@ -513,6 +524,45 @@ impl PhysicalDeviceFeatures { } else { None }, + extended_dynamic_state: if device_api_version < vk::API_VERSION_1_3 + && enabled_extensions.contains(&ext::extended_dynamic_state::NAME) + { + Some( + vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT::default() + .extended_dynamic_state(true), + ) + } else { + None + }, + extended_dynamic_state3: if enabled_extensions + .contains(&ext::extended_dynamic_state3::NAME) + { + Some( + vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT::default() + .extended_dynamic_state3_depth_clamp_enable( + private_caps + .dynamic_state_flags + .contains(DynamicStateFlags::DEPTH_CLAMP_ENABLE), + ) + .extended_dynamic_state3_color_blend_enable( + private_caps + .dynamic_state_flags + .contains(DynamicStateFlags::COLOR_BLEND_ENABLE), + ) + .extended_dynamic_state3_color_blend_equation( + private_caps + .dynamic_state_flags + .contains(DynamicStateFlags::COLOR_BLEND_EQUATION), + ) + .extended_dynamic_state3_polygon_mode( + private_caps + .dynamic_state_flags + .contains(DynamicStateFlags::POLYGON_MODE), + ), + ) + } else { + None + }, } } @@ -1516,6 +1566,13 @@ impl super::InstanceShared { features2 = features2.push_next(next); } + if capabilities.supports_extension(ext::extended_dynamic_state::NAME) { + let next = features + .extended_dynamic_state + .insert(vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT::default()); + features2 = features2.push_next(next); + } + unsafe { get_device_properties.get_physical_device_features2(phd, &mut features2) }; features2.features } else { @@ -1641,6 +1698,30 @@ impl super::Instance { return None; } + let mut dynamic_state_flags = DynamicStateFlags::empty(); + if let Some(feature) = phd_features.extended_dynamic_state { + if feature.extended_dynamic_state == vk::TRUE { + dynamic_state_flags |= DynamicStateFlags::CULL_MODE + | DynamicStateFlags::DEPTH_TEST_ENABLE + | DynamicStateFlags::DEPTH_WRITE_ENABLE + | DynamicStateFlags::FRONT_FACE; + } + } + if let Some(feature) = phd_features.extended_dynamic_state3 { + if feature.extended_dynamic_state3_depth_clamp_enable == vk::TRUE { + dynamic_state_flags |= DynamicStateFlags::DEPTH_CLAMP_ENABLE; + } + if feature.extended_dynamic_state3_color_blend_enable == vk::TRUE { + dynamic_state_flags |= DynamicStateFlags::COLOR_BLEND_ENABLE; + } + if feature.extended_dynamic_state3_color_blend_equation == vk::TRUE { + dynamic_state_flags |= DynamicStateFlags::COLOR_BLEND_EQUATION; + } + if feature.extended_dynamic_state3_polygon_mode == vk::TRUE { + dynamic_state_flags |= DynamicStateFlags::POLYGON_MODE; + } + } + let private_caps = super::PrivateCapabilities { flip_y_requires_shift: phd_capabilities.device_api_version >= vk::API_VERSION_1_1 || phd_capabilities.supports_extension(khr::maintenance1::NAME), @@ -1708,6 +1789,7 @@ impl super::Instance { .properties .limits .max_sampler_allocation_count, + dynamic_state_flags, }; let capabilities = crate::Capabilities { limits: phd_capabilities.to_wgpu_limits(), @@ -1892,6 +1974,25 @@ impl super::Adapter { } else { None }; + let extended_dynamic_state_fns = + if self.phd_capabilities.device_api_version >= vk::API_VERSION_1_3 { + Some(super::ExtensionFn::Promoted) + } else if enabled_extensions.contains(&ext::extended_dynamic_state::NAME) { + Some(super::ExtensionFn::Extension( + ext::extended_dynamic_state::Device::new(&self.instance.raw, &raw_device), + )) + } else { + None + }; + let extended_dynamic_state3_fns = + if enabled_extensions.contains(&ext::extended_dynamic_state3::NAME) { + Some(ext::extended_dynamic_state3::Device::new( + &self.instance.raw, + &raw_device, + )) + } else { + None + }; let naga_options = { use naga::back::spv; @@ -2071,6 +2172,8 @@ impl super::Adapter { timeline_semaphore: timeline_semaphore_fn, ray_tracing: ray_tracing_fns, mesh_shading: mesh_shading_fns, + extended_dynamic_state: extended_dynamic_state_fns, + extended_dynamic_state3: extended_dynamic_state3_fns, }, pipeline_cache_validation_key, vendor_id: self.phd_capabilities.properties.vendor_id, diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs index 03659efba3..f656c21a19 100644 --- a/wgpu-hal/src/vulkan/command.rs +++ b/wgpu-hal/src/vulkan/command.rs @@ -1,4 +1,4 @@ -use super::conv; +use super::{conv, ExtensionFn}; use arrayvec::ArrayVec; use ash::vk; @@ -931,7 +931,138 @@ impl crate::CommandEncoder for super::CommandEncoder { self.active, vk::PipelineBindPoint::GRAPHICS, pipeline.raw, - ) + ); + + for cmd in pipeline.dynamic_state_commands.iter() { + match cmd { + super::DynamicStateCommand::SetStencilMasks { + read_mask, + write_mask, + } => { + self.device.raw.cmd_set_stencil_compare_mask( + self.active, + vk::StencilFaceFlags::FRONT_AND_BACK, + *read_mask, + ); + self.device.raw.cmd_set_stencil_write_mask( + self.active, + vk::StencilFaceFlags::FRONT_AND_BACK, + *write_mask, + ); + } + super::DynamicStateCommand::SetDepthBias { + constant, + clamp, + slope, + } => { + self.device + .raw + .cmd_set_depth_bias(self.active, *constant, *clamp, *slope); + } + super::DynamicStateCommand::SetDepthTest(enable) => { + match self + .device + .extension_fns + .extended_dynamic_state + .as_ref() + .unwrap() + { + ExtensionFn::Promoted => { + self.device + .raw + .cmd_set_depth_test_enable(self.active, *enable); + } + ExtensionFn::Extension(ext) => { + ext.cmd_set_depth_test_enable(self.active, *enable); + } + } + } + super::DynamicStateCommand::SetDepthCompare(compare) => { + match self + .device + .extension_fns + .extended_dynamic_state + .as_ref() + .unwrap() + { + ExtensionFn::Promoted => { + self.device + .raw + .cmd_set_depth_compare_op(self.active, *compare); + } + ExtensionFn::Extension(ext) => { + ext.cmd_set_depth_compare_op(self.active, *compare); + } + } + } + super::DynamicStateCommand::SetDepthWrite(enable) => { + match self + .device + .extension_fns + .extended_dynamic_state + .as_ref() + .unwrap() + { + ExtensionFn::Promoted => { + self.device + .raw + .cmd_set_depth_write_enable(self.active, *enable); + } + ExtensionFn::Extension(ext) => { + ext.cmd_set_depth_write_enable(self.active, *enable); + } + } + } + super::DynamicStateCommand::SetCullMode(mode) => { + match self + .device + .extension_fns + .extended_dynamic_state + .as_ref() + .unwrap() + { + ExtensionFn::Promoted => { + self.device.raw.cmd_set_cull_mode(self.active, *mode); + } + ExtensionFn::Extension(ext) => { + ext.cmd_set_cull_mode(self.active, *mode); + } + } + } + super::DynamicStateCommand::SetFrontFace(mode) => { + match self + .device + .extension_fns + .extended_dynamic_state + .as_ref() + .unwrap() + { + ExtensionFn::Promoted => { + self.device.raw.cmd_set_front_face(self.active, *mode); + } + ExtensionFn::Extension(ext) => { + ext.cmd_set_front_face(self.active, *mode); + } + } + } + super::DynamicStateCommand::SetPolygonMode(mode) => { + self.device + .extension_fns + .extended_dynamic_state3 + .as_ref() + .unwrap() + .cmd_set_polygon_mode(self.active, *mode); + } + super::DynamicStateCommand::SetDepthClampEnable(enable) => { + self.device + .extension_fns + .extended_dynamic_state3 + .as_ref() + .unwrap() + .cmd_set_depth_clamp_enable(self.active, *enable); + } + } + } }; } diff --git a/wgpu-hal/src/vulkan/conv.rs b/wgpu-hal/src/vulkan/conv.rs index e1d5cb30e8..b6197e5d07 100644 --- a/wgpu-hal/src/vulkan/conv.rs +++ b/wgpu-hal/src/vulkan/conv.rs @@ -803,10 +803,11 @@ pub fn map_front_face(front_face: wgt::FrontFace) -> vk::FrontFace { } } -pub fn map_cull_face(face: wgt::Face) -> vk::CullModeFlags { +pub fn map_cull_face(face: Option) -> vk::CullModeFlags { match face { - wgt::Face::Front => vk::CullModeFlags::FRONT, - wgt::Face::Back => vk::CullModeFlags::BACK, + Some(wgt::Face::Front) => vk::CullModeFlags::FRONT, + Some(wgt::Face::Back) => vk::CullModeFlags::BACK, + None => vk::CullModeFlags::NONE, } } @@ -824,18 +825,14 @@ pub fn map_stencil_op(op: wgt::StencilOperation) -> vk::StencilOp { } } -pub fn map_stencil_face( - face: &wgt::StencilFaceState, - compare_mask: u32, - write_mask: u32, -) -> vk::StencilOpState { +pub fn map_stencil_face(face: &wgt::StencilFaceState) -> vk::StencilOpState { vk::StencilOpState { fail_op: map_stencil_op(face.fail_op), pass_op: map_stencil_op(face.pass_op), depth_fail_op: map_stencil_op(face.depth_fail_op), compare_op: map_comparison(face.compare), - compare_mask, - write_mask, + compare_mask: 0, + write_mask: 0, reference: 0, } } diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index d12748fcfd..f5a514bf74 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -14,7 +14,7 @@ use ash::{khr, vk}; use hashbrown::hash_map::Entry; use parking_lot::Mutex; -use super::{conv, RawTlasInstance}; +use super::{conv, DynamicStateCommand, RawTlasInstance}; use crate::TlasInstance; impl super::DeviceShared { @@ -1895,7 +1895,7 @@ impl crate::Device for super::Device { super::PipelineCache, >, ) -> Result { - let dynamic_states = [ + let mut dynamic_states = vec![ vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR, vk::DynamicState::BLEND_CONSTANTS, @@ -1909,6 +1909,7 @@ impl crate::Device for super::Device { let mut stages = ArrayVec::<_, { crate::MAX_CONCURRENT_SHADER_STAGES }>::new(); let mut vertex_buffers = Vec::with_capacity(desc.vertex_buffers.len()); let mut vertex_attributes = Vec::new(); + let mut dynamic_state_commands = Vec::new(); for (i, vb) in desc.vertex_buffers.iter().enumerate() { vertex_buffers.push(vk::VertexInputBindingDescription { @@ -1956,14 +1957,68 @@ impl crate::Device for super::Device { None => None, }; - let mut vk_rasterization = vk::PipelineRasterizationStateCreateInfo::default() - .polygon_mode(conv::map_polygon_mode(desc.primitive.polygon_mode)) - .front_face(conv::map_front_face(desc.primitive.front_face)) - .line_width(1.0) - .depth_clamp_enable(desc.primitive.unclipped_depth); - if let Some(face) = desc.primitive.cull_mode { - vk_rasterization = vk_rasterization.cull_mode(conv::map_cull_face(face)) + let mut vk_rasterization = + vk::PipelineRasterizationStateCreateInfo::default().line_width(1.0); + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::POLYGON_MODE) + { + dynamic_states.push(vk::DynamicState::POLYGON_MODE_EXT); + dynamic_state_commands.push(DynamicStateCommand::SetPolygonMode( + conv::map_polygon_mode(desc.primitive.polygon_mode), + )); + } else { + vk_rasterization = + vk_rasterization.polygon_mode(conv::map_polygon_mode(desc.primitive.polygon_mode)); + } + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::FRONT_FACE) + { + dynamic_states.push(vk::DynamicState::FRONT_FACE); + dynamic_state_commands.push(DynamicStateCommand::SetFrontFace(conv::map_front_face( + desc.primitive.front_face, + ))); + } else { + vk_rasterization = + vk_rasterization.front_face(conv::map_front_face(desc.primitive.front_face)); + } + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::DEPTH_CLAMP_ENABLE) + { + dynamic_states.push(vk::DynamicState::DEPTH_CLAMP_ENABLE_EXT); + dynamic_state_commands.push(DynamicStateCommand::SetDepthClampEnable( + desc.primitive.unclipped_depth, + )); + } else { + vk_rasterization = vk_rasterization.depth_clamp_enable(desc.primitive.unclipped_depth); } + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::CULL_MODE) + { + dynamic_states.push(vk::DynamicState::CULL_MODE); + dynamic_state_commands.push(DynamicStateCommand::SetCullMode(conv::map_cull_face( + desc.primitive.cull_mode, + ))); + } else { + vk_rasterization = + vk_rasterization.cull_mode(conv::map_cull_face(desc.primitive.cull_mode)); + } + let mut vk_rasterization_conservative_state = vk::PipelineRasterizationConservativeStateCreateInfoEXT::default() .conservative_rasterization_mode( @@ -1973,7 +2028,8 @@ impl crate::Device for super::Device { vk_rasterization = vk_rasterization.push_next(&mut vk_rasterization_conservative_state); } - let mut vk_depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default(); + let mut vk_depth_stencil: vk::PipelineDepthStencilStateCreateInfo<'_> = + vk::PipelineDepthStencilStateCreateInfo::default(); if let Some(ref ds) = desc.depth_stencil { let vk_format = self.shared.private_caps.map_texture_format(ds.format); let vk_layout = if ds.is_read_only(desc.primitive.cull_mode) { @@ -1986,28 +2042,76 @@ impl crate::Device for super::Device { stencil_ops: crate::AttachmentOps::all(), }); - if ds.is_depth_enabled() { - vk_depth_stencil = vk_depth_stencil - .depth_test_enable(true) - .depth_write_enable(ds.depth_write_enabled) - .depth_compare_op(conv::map_comparison(ds.depth_compare)); + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::DEPTH_TEST_ENABLE) + { + dynamic_states.push(vk::DynamicState::DEPTH_TEST_ENABLE); + dynamic_state_commands + .push(DynamicStateCommand::SetDepthTest(ds.is_depth_enabled())); + } else { + vk_depth_stencil = vk_depth_stencil.depth_test_enable(ds.is_depth_enabled()); + } + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::DEPTH_WRITE_ENABLE) + { + dynamic_states.push(vk::DynamicState::DEPTH_WRITE_ENABLE); + dynamic_state_commands + .push(DynamicStateCommand::SetDepthWrite(ds.depth_write_enabled)); + } else if ds.is_depth_enabled() { + vk_depth_stencil = vk_depth_stencil.depth_write_enable(ds.depth_write_enabled); } + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::DEPTH_COMPARE_OP) + { + dynamic_states.push(vk::DynamicState::DEPTH_COMPARE_OP); + dynamic_state_commands.push(DynamicStateCommand::SetDepthCompare( + conv::map_comparison(ds.depth_compare), + )); + } else { + vk_depth_stencil = + vk_depth_stencil.depth_compare_op(conv::map_comparison(ds.depth_compare)); + } + + if ds.is_depth_enabled() && ds.bias.is_enabled() { + dynamic_states.push(vk::DynamicState::DEPTH_BIAS); + + vk_rasterization = vk_rasterization.depth_bias_enable(true); + + dynamic_state_commands.push(DynamicStateCommand::SetDepthBias { + constant: ds.bias.constant as f32, + clamp: ds.bias.clamp, + slope: ds.bias.slope_scale, + }); + } + if ds.stencil.is_enabled() { let s = &ds.stencil; - let front = conv::map_stencil_face(&s.front, s.read_mask, s.write_mask); - let back = conv::map_stencil_face(&s.back, s.read_mask, s.write_mask); + + dynamic_states.push(vk::DynamicState::STENCIL_COMPARE_MASK); + dynamic_states.push(vk::DynamicState::STENCIL_WRITE_MASK); + + let front = conv::map_stencil_face(&s.front); + let back = conv::map_stencil_face(&s.back); vk_depth_stencil = vk_depth_stencil .stencil_test_enable(true) .front(front) .back(back); - } - if ds.bias.is_enabled() { - vk_rasterization = vk_rasterization - .depth_bias_enable(true) - .depth_bias_constant_factor(ds.bias.constant as f32) - .depth_bias_clamp(ds.bias.clamp) - .depth_bias_slope_factor(ds.bias.slope_scale); + dynamic_state_commands.push(DynamicStateCommand::SetStencilMasks { + read_mask: s.read_mask, + write_mask: s.write_mask, + }); } } @@ -2118,7 +2222,10 @@ impl crate::Device for super::Device { self.counters.render_pipelines.add(1); - Ok(super::RenderPipeline { raw }) + Ok(super::RenderPipeline { + raw, + dynamic_state_commands, + }) } unsafe fn create_mesh_pipeline( &self, @@ -2128,7 +2235,7 @@ impl crate::Device for super::Device { ::PipelineCache, >, ) -> Result<::RenderPipeline, crate::PipelineError> { - let dynamic_states = [ + let mut dynamic_states = vec![ vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR, vk::DynamicState::BLEND_CONSTANTS, @@ -2140,10 +2247,12 @@ impl crate::Device for super::Device { ..Default::default() }; let mut stages = ArrayVec::<_, { crate::MAX_CONCURRENT_SHADER_STAGES }>::new(); + let mut dynamic_state_commands = Vec::new(); - let vk_input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default() - .topology(conv::map_topology(desc.primitive.topology)) - .primitive_restart_enable(desc.primitive.strip_index_format.is_some()); + let vk_input_assembly: vk::PipelineInputAssemblyStateCreateInfo<'_> = + vk::PipelineInputAssemblyStateCreateInfo::default() + .topology(conv::map_topology(desc.primitive.topology)) + .primitive_restart_enable(desc.primitive.strip_index_format.is_some()); let compiled_ts = match desc.task_stage { Some(ref stage) => { @@ -2181,14 +2290,53 @@ impl crate::Device for super::Device { None => None, }; - let mut vk_rasterization = vk::PipelineRasterizationStateCreateInfo::default() - .polygon_mode(conv::map_polygon_mode(desc.primitive.polygon_mode)) - .front_face(conv::map_front_face(desc.primitive.front_face)) - .line_width(1.0) - .depth_clamp_enable(desc.primitive.unclipped_depth); - if let Some(face) = desc.primitive.cull_mode { - vk_rasterization = vk_rasterization.cull_mode(conv::map_cull_face(face)) + let mut vk_rasterization = + vk::PipelineRasterizationStateCreateInfo::default().line_width(1.0); + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::FRONT_FACE) + { + dynamic_states.push(vk::DynamicState::FRONT_FACE); + dynamic_state_commands.push(DynamicStateCommand::SetFrontFace(conv::map_front_face( + desc.primitive.front_face, + ))); + } else { + vk_rasterization = + vk_rasterization.front_face(conv::map_front_face(desc.primitive.front_face)); } + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::DEPTH_CLAMP_ENABLE) + { + dynamic_states.push(vk::DynamicState::DEPTH_CLAMP_ENABLE_EXT); + dynamic_state_commands.push(DynamicStateCommand::SetDepthClampEnable( + desc.primitive.unclipped_depth, + )); + } else { + vk_rasterization = vk_rasterization.depth_clamp_enable(desc.primitive.unclipped_depth); + } + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::CULL_MODE) + { + dynamic_states.push(vk::DynamicState::CULL_MODE); + dynamic_state_commands.push(DynamicStateCommand::SetCullMode(conv::map_cull_face( + desc.primitive.cull_mode, + ))); + } else { + vk_rasterization = + vk_rasterization.cull_mode(conv::map_cull_face(desc.primitive.cull_mode)); + } + let mut vk_rasterization_conservative_state = vk::PipelineRasterizationConservativeStateCreateInfoEXT::default() .conservative_rasterization_mode( @@ -2211,28 +2359,82 @@ impl crate::Device for super::Device { stencil_ops: crate::AttachmentOps::all(), }); - if ds.is_depth_enabled() { - vk_depth_stencil = vk_depth_stencil - .depth_test_enable(true) - .depth_write_enable(ds.depth_write_enabled) - .depth_compare_op(conv::map_comparison(ds.depth_compare)); + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::DEPTH_TEST_ENABLE) + { + dynamic_states.push(vk::DynamicState::DEPTH_TEST_ENABLE); + dynamic_state_commands + .push(DynamicStateCommand::SetDepthTest(ds.is_depth_enabled())); + } else { + vk_depth_stencil = vk_depth_stencil.depth_test_enable(ds.is_depth_enabled()); } + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::DEPTH_WRITE_ENABLE) + { + dynamic_states.push(vk::DynamicState::DEPTH_WRITE_ENABLE); + + if ds.is_depth_enabled() { + dynamic_state_commands + .push(DynamicStateCommand::SetDepthWrite(ds.depth_write_enabled)); + } + } else if ds.is_depth_enabled() { + vk_depth_stencil = vk_depth_stencil.depth_write_enable(ds.depth_write_enabled); + } + + if self + .shared + .private_caps + .dynamic_state_flags + .contains(super::DynamicStateFlags::DEPTH_COMPARE_OP) + { + dynamic_states.push(vk::DynamicState::DEPTH_COMPARE_OP); + + if ds.is_depth_enabled() { + dynamic_state_commands.push(DynamicStateCommand::SetDepthCompare( + conv::map_comparison(ds.depth_compare), + )); + } + } else { + vk_depth_stencil = + vk_depth_stencil.depth_compare_op(conv::map_comparison(ds.depth_compare)); + } + + if ds.is_depth_enabled() && ds.bias.is_enabled() { + dynamic_states.push(vk::DynamicState::DEPTH_BIAS); + + vk_rasterization = vk_rasterization.depth_bias_enable(true); + + dynamic_state_commands.push(DynamicStateCommand::SetDepthBias { + constant: ds.bias.constant as f32, + clamp: ds.bias.clamp, + slope: ds.bias.slope_scale, + }); + } + if ds.stencil.is_enabled() { let s = &ds.stencil; - let front = conv::map_stencil_face(&s.front, s.read_mask, s.write_mask); - let back = conv::map_stencil_face(&s.back, s.read_mask, s.write_mask); + + dynamic_states.push(vk::DynamicState::STENCIL_COMPARE_MASK); + dynamic_states.push(vk::DynamicState::STENCIL_WRITE_MASK); + + let front = conv::map_stencil_face(&s.front); + let back = conv::map_stencil_face(&s.back); vk_depth_stencil = vk_depth_stencil .stencil_test_enable(true) .front(front) .back(back); - } - if ds.bias.is_enabled() { - vk_rasterization = vk_rasterization - .depth_bias_enable(true) - .depth_bias_constant_factor(ds.bias.constant as f32) - .depth_bias_clamp(ds.bias.clamp) - .depth_bias_slope_factor(ds.bias.slope_scale); + dynamic_state_commands.push(DynamicStateCommand::SetStencilMasks { + read_mask: s.read_mask, + write_mask: s.write_mask, + }); } } @@ -2349,7 +2551,10 @@ impl crate::Device for super::Device { self.counters.render_pipelines.add(1); - Ok(super::RenderPipeline { raw }) + Ok(super::RenderPipeline { + raw, + dynamic_state_commands, + }) } unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) { diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index ef349c7b26..5303a7b942 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -485,6 +485,8 @@ struct DeviceExtensionFunctions { timeline_semaphore: Option>, ray_tracing: Option, mesh_shading: Option, + extended_dynamic_state: Option>, + extended_dynamic_state3: Option, } struct RayTracingDeviceExtensionFunctions { @@ -492,6 +494,25 @@ struct RayTracingDeviceExtensionFunctions { buffer_device_address: khr::buffer_device_address::Device, } +bitflags::bitflags!( + // Subset of VK_EXT_extended_dynamic_state, VK_EXT_extended_dynamic_state2, etc. flags that matter for wgpu. + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub struct DynamicStateFlags: u32 { + // VK_EXT_extended_dynamic_state + const CULL_MODE = 1 << 0; + const DEPTH_TEST_ENABLE = 1 << 1; + const DEPTH_WRITE_ENABLE = 1 << 2; + const DEPTH_COMPARE_OP = 1 << 3; + const FRONT_FACE = 1 << 4; + + // VK_EXT_extended_dynamic_state3 + const DEPTH_CLAMP_ENABLE = 1 << 5; + const COLOR_BLEND_ENABLE = 1 << 6; + const COLOR_BLEND_EQUATION = 1 << 7; + const POLYGON_MODE = 1 << 8; + } +); + /// Set of internal capabilities, which don't show up in the exposed /// device geometry, but affect the code paths taken internally. #[derive(Clone, Debug)] @@ -543,6 +564,8 @@ struct PrivateCapabilities { zero_initialize_workgroup_memory: bool, image_format_list: bool, maximum_samplers: u32, + + dynamic_state_flags: DynamicStateFlags, } bitflags::bitflags!( @@ -997,11 +1020,32 @@ pub enum ShaderModule { }, } +#[derive(Debug)] +pub enum DynamicStateCommand { + SetStencilMasks { + read_mask: u32, + write_mask: u32, + }, + SetDepthBias { + constant: f32, + clamp: f32, + slope: f32, + }, + SetDepthTest(bool), + SetDepthCompare(vk::CompareOp), + SetDepthWrite(bool), + SetCullMode(vk::CullModeFlags), + SetFrontFace(vk::FrontFace), + SetPolygonMode(vk::PolygonMode), + SetDepthClampEnable(bool), +} + impl crate::DynShaderModule for ShaderModule {} #[derive(Debug)] pub struct RenderPipeline { raw: vk::Pipeline, + dynamic_state_commands: Vec, } impl crate::DynRenderPipeline for RenderPipeline {}