diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 41ad76a90..d4f9e88ab 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -36,7 +36,7 @@ thiserror = "1" [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "93db57c" +rev = "d4bedaf" #version = "0.6" features = ["wgsl-in"] diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 4646eaa97..e0fe942f2 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -69,12 +69,12 @@ core-graphics-types = "0.1" [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "93db57c" +rev = "d4bedaf" #version = "0.6" [dev-dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "93db57c" +rev = "d4bedaf" #version = "0.6" features = ["wgsl-in"] diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 5de00bc06..3e0821286 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -53,13 +53,11 @@ impl PhysicalDeviceFeatures { downlevel_flags: wgt::DownlevelFlags, private_caps: &super::PrivateCapabilities, ) -> Self { - //TODO: make configurable - let rba = !(cfg!(target_os = "macos") || cfg!(target_os = "ios")); Self { // vk::PhysicalDeviceFeatures is a struct composed of Bool32's while // Features is a bitfield so we need to map everything manually core: vk::PhysicalDeviceFeatures::builder() - .robust_buffer_access(rba) + .robust_buffer_access(private_caps.robust_buffer_access) .independent_blend(true) .sample_rate_shading(true) .image_cube_array( @@ -631,6 +629,7 @@ impl super::Instance { }; let (available_features, downlevel_flags) = phd_features.to_wgpu(&phd_capabilities); + let mut workarounds = super::Workarounds::empty(); { use crate::auxil::db; // see https://github.com/gfx-rs/gfx/issues/1930 @@ -640,6 +639,8 @@ impl super::Instance { == db::intel::DEVICE_KABY_LAKE_MASK || phd_capabilities.properties.device_id & db::intel::DEVICE_SKY_LAKE_MASK == db::intel::DEVICE_SKY_LAKE_MASK); + // TODO: only enable for particular devices + workarounds |= super::Workarounds::SEPARATE_ENTRY_POINTS; }; if phd_features.core.sample_rate_shading == 0 { @@ -700,6 +701,8 @@ impl super::Instance { }, non_coherent_map_mask: phd_capabilities.properties.limits.non_coherent_atom_size - 1, can_present: true, + //TODO: make configurable + robust_buffer_access: phd_features.core.robust_buffer_access != 0, }; let capabilities = crate::Capabilities { @@ -725,6 +728,7 @@ impl super::Instance { //phd_features, downlevel_flags, private_caps, + workarounds, }; Some(crate::ExposedAdapter { @@ -845,8 +849,15 @@ impl super::Adapter { lang_version: (1, 0), flags, capabilities: Some(capabilities.iter().cloned().collect()), - index_bounds_check_policy: naga::back::BoundsCheckPolicy::Restrict, - image_bounds_check_policy: naga::back::BoundsCheckPolicy::Restrict, + bounds_check_policies: naga::back::BoundsCheckPolicies { + index: naga::back::BoundsCheckPolicy::Restrict, + image: naga::back::BoundsCheckPolicy::Restrict, + buffer: if self.private_caps.robust_buffer_access { + naga::back::BoundsCheckPolicy::Unchecked + } else { + naga::back::BoundsCheckPolicy::Restrict + }, + }, } }; @@ -864,6 +875,7 @@ impl super::Adapter { vendor_id: self.phd_capabilities.properties.vendor_id, downlevel_flags: self.downlevel_flags, private_caps: self.private_caps.clone(), + workarounds: self.workarounds, timestamp_period: self.phd_capabilities.properties.limits.timestamp_period, render_passes: Mutex::new(Default::default()), framebuffers: Mutex::new(Default::default()), diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 5d145fd4e..7d53ef8a3 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -460,6 +460,12 @@ impl } } +struct CompiledStage { + create_info: vk::PipelineShaderStageCreateInfo, + _entry_point: CString, + temp_raw_module: Option, +} + impl super::Device { pub(super) unsafe fn create_swapchain( &self, @@ -555,6 +561,59 @@ impl super::Device { copy_size: conv::map_extent_to_copy_size(&desc.size, desc.dimension), } } + + fn create_shader_module_impl( + &self, + spv: &[u32], + ) -> Result { + let vk_info = vk::ShaderModuleCreateInfo::builder() + .flags(vk::ShaderModuleCreateFlags::empty()) + .code(spv); + + let raw = unsafe { self.shared.raw.create_shader_module(&vk_info, None)? }; + Ok(raw) + } + + fn compile_stage( + &self, + stage: &crate::ProgrammableStage, + naga_stage: naga::ShaderStage, + ) -> Result { + let stage_flags = crate::auxil::map_naga_stage(naga_stage); + let vk_module = match *stage.module { + super::ShaderModule::Raw(raw) => raw, + super::ShaderModule::Intermediate(ref naga_shader) => { + let pipeline_options = naga::back::spv::PipelineOptions { + entry_point: stage.entry_point.to_string(), + shader_stage: naga_stage, + }; + let spv = naga::back::spv::write_vec( + &naga_shader.module, + &naga_shader.info, + &self.naga_options, + Some(&pipeline_options), + ) + .map_err(|e| crate::PipelineError::Linkage(stage_flags, format!("{}", e)))?; + self.create_shader_module_impl(&spv)? + } + }; + + let entry_point = CString::new(stage.entry_point).unwrap(); + let create_info = vk::PipelineShaderStageCreateInfo::builder() + .stage(conv::map_shader_stage(stage_flags)) + .module(vk_module) + .name(&entry_point) + .build(); + + Ok(CompiledStage { + create_info, + _entry_point: entry_point, + temp_raw_module: match *stage.module { + super::ShaderModule::Raw(_) => None, + super::ShaderModule::Intermediate(_) => Some(vk_module), + }, + }) + } } impl crate::Device for super::Device { @@ -1114,36 +1173,43 @@ impl crate::Device for super::Device { shader: crate::ShaderInput, ) -> Result { let spv = match shader { - crate::ShaderInput::Naga(naga_shader) => Cow::Owned( - naga::back::spv::write_vec( - &naga_shader.module, - &naga_shader.info, - &self.naga_options, + crate::ShaderInput::Naga(naga_shader) => { + if self + .shared + .workarounds + .contains(super::Workarounds::SEPARATE_ENTRY_POINTS) + { + return Ok(super::ShaderModule::Intermediate(naga_shader)); + } + Cow::Owned( + naga::back::spv::write_vec( + &naga_shader.module, + &naga_shader.info, + &self.naga_options, + None, + ) + .map_err(|e| crate::ShaderError::Compilation(format!("{}", e)))?, ) - .map_err(|e| crate::ShaderError::Compilation(format!("{}", e)))?, - ), + } crate::ShaderInput::SpirV(spv) => Cow::Borrowed(spv), }; - let vk_info = vk::ShaderModuleCreateInfo::builder() - .flags(vk::ShaderModuleCreateFlags::empty()) - .code(&spv); - - let raw = self - .shared - .raw - .create_shader_module(&vk_info, None) - .map_err(crate::DeviceError::from)?; + let raw = self.create_shader_module_impl(&*spv)?; if let Some(label) = desc.label { self.shared .set_object_name(vk::ObjectType::SHADER_MODULE, raw, label); } - Ok(super::ShaderModule { raw }) + Ok(super::ShaderModule::Raw(raw)) } unsafe fn destroy_shader_module(&self, module: super::ShaderModule) { - let _ = self.shared.raw.destroy_shader_module(module.raw, None); + match module { + super::ShaderModule::Raw(raw) => { + let _ = self.shared.raw.destroy_shader_module(raw, None); + } + super::ShaderModule::Intermediate(_) => {} + } } unsafe fn create_render_pipeline( @@ -1193,29 +1259,16 @@ impl crate::Device for super::Device { .primitive_restart_enable(desc.primitive.strip_index_format.is_some()) .build(); - let vs_entry_point; - { - let stage = &desc.vertex_stage; - vs_entry_point = CString::new(stage.entry_point).unwrap(); - stages.push( - vk::PipelineShaderStageCreateInfo::builder() - .stage(vk::ShaderStageFlags::VERTEX) - .module(stage.module.raw) - .name(&vs_entry_point) - .build(), - ); - } - let fs_entry_point; - if let Some(ref stage) = desc.fragment_stage { - fs_entry_point = CString::new(stage.entry_point).unwrap(); - stages.push( - vk::PipelineShaderStageCreateInfo::builder() - .stage(vk::ShaderStageFlags::FRAGMENT) - .module(stage.module.raw) - .name(&fs_entry_point) - .build(), - ); - } + let compiled_vs = self.compile_stage(&desc.vertex_stage, naga::ShaderStage::Vertex)?; + stages.push(compiled_vs.create_info); + let compiled_fs = match desc.fragment_stage { + Some(ref stage) => { + let compiled = self.compile_stage(stage, naga::ShaderStage::Fragment)?; + stages.push(compiled.create_info); + Some(compiled) + } + None => None, + }; let mut vk_rasterization = vk::PipelineRasterizationStateCreateInfo::builder() .depth_clamp_enable(desc.primitive.clamp_depth) @@ -1355,6 +1408,17 @@ impl crate::Device for super::Device { .set_object_name(vk::ObjectType::PIPELINE, raw, label); } + if let Some(raw_module) = compiled_vs.temp_raw_module { + self.shared.raw.destroy_shader_module(raw_module, None); + } + if let Some(CompiledStage { + temp_raw_module: Some(raw_module), + .. + }) = compiled_fs + { + self.shared.raw.destroy_shader_module(raw_module, None); + } + Ok(super::RenderPipeline { raw }) } unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) { @@ -1365,17 +1429,12 @@ impl crate::Device for super::Device { &self, desc: &crate::ComputePipelineDescriptor, ) -> Result { - let cs_entry_point = CString::new(desc.stage.entry_point).unwrap(); - let vk_stage = vk::PipelineShaderStageCreateInfo::builder() - .stage(vk::ShaderStageFlags::COMPUTE) - .module(desc.stage.module.raw) - .name(&cs_entry_point) - .build(); + let compiled = self.compile_stage(&desc.stage, naga::ShaderStage::Compute)?; let vk_infos = [{ vk::ComputePipelineCreateInfo::builder() .layout(desc.layout.raw) - .stage(vk_stage) + .stage(compiled.create_info) .build() }]; @@ -1391,6 +1450,10 @@ impl crate::Device for super::Device { .set_object_name(vk::ObjectType::PIPELINE, raw, label); } + if let Some(raw_module) = compiled.temp_raw_module { + self.shared.raw.destroy_shader_module(raw_module, None); + } + Ok(super::ComputePipeline { raw }) } unsafe fn destroy_compute_pipeline(&self, pipeline: super::ComputePipeline) { diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index e76e5d43e..1b18863d6 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -130,6 +130,7 @@ pub struct Adapter { //phd_features: adapter::PhysicalDeviceFeatures, downlevel_flags: wgt::DownlevelFlags, private_caps: PrivateCapabilities, + workarounds: Workarounds, } // TODO there's no reason why this can't be unified--the function pointers should all be the same--it's not clear how to do this with `ash`. @@ -161,8 +162,17 @@ struct PrivateCapabilities { /// Ability to present contents to any screen. Only needed to work around broken platform configurations. can_present: bool, non_coherent_map_mask: wgt::BufferAddress, + robust_buffer_access: bool, } +bitflags::bitflags!( + /// Workaround flags. + pub struct Workarounds: u32 { + /// Only generate SPIR-V for one entry point at a time. + const SEPARATE_ENTRY_POINTS = 0x1; + } +); + #[derive(Clone, Debug, Eq, Hash, PartialEq)] struct AttachmentKey { format: vk::Format, @@ -225,6 +235,7 @@ struct DeviceShared { timestamp_period: f32, downlevel_flags: wgt::DownlevelFlags, private_caps: PrivateCapabilities, + workarounds: Workarounds, render_passes: Mutex>, framebuffers: Mutex>, } @@ -358,8 +369,9 @@ pub struct CommandBuffer { } #[derive(Debug)] -pub struct ShaderModule { - raw: vk::ShaderModule, +pub enum ShaderModule { + Raw(vk::ShaderModule), + Intermediate(crate::NagaShader), } #[derive(Debug)] diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index bd626b5a7..d37e2dc20 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -90,14 +90,14 @@ env_logger = "0.8" [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "93db57c" +rev = "d4bedaf" #version = "0.6" optional = true # used to test all the example shaders [dev-dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "93db57c" +rev = "d4bedaf" #version = "0.6" features = ["wgsl-in"]