From 418b9ff547cad151fd8a9e53bdd49709e5104ff2 Mon Sep 17 00:00:00 2001 From: Jamie Nicol Date: Wed, 23 Jul 2025 16:33:47 +0100 Subject: [PATCH 1/2] [vulkan] Use corresponding bind group layout entries when creating bind groups, rather than list of vk::DescriptorType This is a refactoring of Vulkan BindGroupLayout and BindGroup creation in preparation for implementing external texture support. Currently when creating a BindGroupLayout the Vulkan backend creates a list of the vk::DescriptorType for each entry, as well as the count, for binding arrays. Then when creating the BindGroup, it iterates through this list and does whatever it needs to do for each entry based on these values. In order to support external textures, we will have to create multiple descriptors for each BindingType::ExternalTexture. This means we cannot map each binding type to a single Vulkan descriptor type. Instead, store the list of BindGroupLayoutEntries on the BindGroupLayout and use those when creating the BindGroup, using the same "layout_and_entry_iter" idiom used by other backends. In subsequent patches this will allow us to create multiple descriptors for a single resource binding. --- wgpu-hal/src/vulkan/device.rs | 170 +++++++++++++++++----------------- wgpu-hal/src/vulkan/mod.rs | 3 +- 2 files changed, 87 insertions(+), 86 deletions(-) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 1297e57f09..a1a74b0230 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -1480,20 +1480,46 @@ impl crate::Device for super::Device { &self, desc: &crate::BindGroupLayoutDescriptor, ) -> Result { + // Iterate through the entries and accumulate our Vulkan + // DescriptorSetLayoutBindings and DescriptorBindingFlags, as well as + // the list of which bindings are binding arrays, and our descriptor + // counts. + // Note: not bothering with on stack arrays here as it's low frequency + let mut vk_bindings = Vec::new(); + let mut binding_flags = Vec::new(); + let mut binding_arrays = Vec::new(); let mut desc_count = gpu_descriptor::DescriptorTotalCount::default(); - let mut types = Vec::new(); - for entry in desc.entries { + for (i, entry) in desc.entries.iter().enumerate() { + if let Some(count) = entry.count { + binding_arrays.push((i as u32, count)) + } + + let partially_bound = desc + .flags + .contains(crate::BindGroupLayoutFlags::PARTIALLY_BOUND); + let mut flags = vk::DescriptorBindingFlags::empty(); + if partially_bound && entry.count.is_some() { + flags |= vk::DescriptorBindingFlags::PARTIALLY_BOUND; + } + if entry.count.is_some() { + flags |= vk::DescriptorBindingFlags::UPDATE_AFTER_BIND; + } + let count = entry.count.map_or(1, |c| c.get()); - if entry.binding as usize >= types.len() { - types.resize( - entry.binding as usize + 1, - (vk::DescriptorType::INPUT_ATTACHMENT, 0), - ); + match entry.ty { + wgt::BindingType::ExternalTexture => unimplemented!(), + _ => { + vk_bindings.push(vk::DescriptorSetLayoutBinding { + binding: entry.binding, + descriptor_type: conv::map_binding_type(entry.ty), + descriptor_count: count, + stage_flags: conv::map_shader_stage(entry.visibility), + p_immutable_samplers: ptr::null(), + _marker: Default::default(), + }); + binding_flags.push(flags); + } } - types[entry.binding as usize] = ( - conv::map_binding_type(entry.ty), - entry.count.map_or(1, |c| c.get()), - ); match entry.ty { wgt::BindingType::Buffer { @@ -1532,27 +1558,6 @@ impl crate::Device for super::Device { } } - //Note: not bothering with on stack array here as it's low frequency - let vk_bindings = desc - .entries - .iter() - .map(|entry| vk::DescriptorSetLayoutBinding { - binding: entry.binding, - descriptor_type: types[entry.binding as usize].0, - descriptor_count: types[entry.binding as usize].1, - stage_flags: conv::map_shader_stage(entry.visibility), - p_immutable_samplers: ptr::null(), - _marker: Default::default(), - }) - .collect::>(); - - let binding_arrays: Vec<_> = desc - .entries - .iter() - .enumerate() - .filter_map(|(idx, entry)| entry.count.map(|count| (idx as u32, count))) - .collect(); - let vk_info = vk::DescriptorSetLayoutCreateInfo::default() .bindings(&vk_bindings) .flags(if !binding_arrays.is_empty() { @@ -1561,30 +1566,8 @@ impl crate::Device for super::Device { vk::DescriptorSetLayoutCreateFlags::empty() }); - let partially_bound = desc - .flags - .contains(crate::BindGroupLayoutFlags::PARTIALLY_BOUND); - - let binding_flag_vec = desc - .entries - .iter() - .map(|entry| { - let mut flags = vk::DescriptorBindingFlags::empty(); - - if partially_bound && entry.count.is_some() { - flags |= vk::DescriptorBindingFlags::PARTIALLY_BOUND; - } - - if entry.count.is_some() { - flags |= vk::DescriptorBindingFlags::UPDATE_AFTER_BIND; - } - - flags - }) - .collect::>(); - - let mut binding_flag_info = vk::DescriptorSetLayoutBindingFlagsCreateInfo::default() - .binding_flags(&binding_flag_vec); + let mut binding_flag_info = + vk::DescriptorSetLayoutBindingFlagsCreateInfo::default().binding_flags(&binding_flags); let vk_info = vk_info.push_next(&mut binding_flag_info); @@ -1604,7 +1587,7 @@ impl crate::Device for super::Device { Ok(super::BindGroupLayout { raw, desc_count, - types: types.into_boxed_slice(), + entries: desc.entries.into(), binding_arrays, }) } @@ -1787,18 +1770,21 @@ impl crate::Device for super::Device { Vec::with_capacity(desc.acceleration_structures.len()); let mut raw_acceleration_structures = ExtendStack::from_vec_capacity(&mut raw_acceleration_structures); - for entry in desc.entries { - let (ty, size) = desc.layout.types[entry.binding as usize]; - if size == 0 { - continue; // empty slot - } - let mut write = vk::WriteDescriptorSet::default() - .dst_set(*set.raw()) - .dst_binding(entry.binding) - .descriptor_type(ty); - - write = match ty { - vk::DescriptorType::SAMPLER => { + + let layout_and_entry_iter = desc.entries.iter().map(|entry| { + let layout = desc + .layout + .entries + .iter() + .find(|layout_entry| layout_entry.binding == entry.binding) + .expect("internal error: no layout entry found with binding slot"); + (layout, entry) + }); + for (layout, entry) in layout_and_entry_iter { + let write = vk::WriteDescriptorSet::default().dst_set(*set.raw()); + + match layout.ty { + wgt::BindingType::Sampler(_) => { let start = entry.resource_index; let end = start + entry.count; let local_image_infos; @@ -1806,9 +1792,14 @@ impl crate::Device for super::Device { image_infos.extend(desc.samplers[start as usize..end as usize].iter().map( |sampler| vk::DescriptorImageInfo::default().sampler(sampler.raw), )); - write.image_info(local_image_infos) + writes.push( + write + .dst_binding(entry.binding) + .descriptor_type(conv::map_binding_type(layout.ty)) + .image_info(local_image_infos), + ); } - vk::DescriptorType::SAMPLED_IMAGE | vk::DescriptorType::STORAGE_IMAGE => { + wgt::BindingType::Texture { .. } | wgt::BindingType::StorageTexture { .. } => { let start = entry.resource_index; let end = start + entry.count; let local_image_infos; @@ -1822,12 +1813,14 @@ impl crate::Device for super::Device { .image_layout(layout) }, )); - write.image_info(local_image_infos) + writes.push( + write + .dst_binding(entry.binding) + .descriptor_type(conv::map_binding_type(layout.ty)) + .image_info(local_image_infos), + ); } - vk::DescriptorType::UNIFORM_BUFFER - | vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC - | vk::DescriptorType::STORAGE_BUFFER - | vk::DescriptorType::STORAGE_BUFFER_DYNAMIC => { + wgt::BindingType::Buffer { .. } => { let start = entry.resource_index; let end = start + entry.count; let local_buffer_infos; @@ -1842,9 +1835,14 @@ impl crate::Device for super::Device { ) }, )); - write.buffer_info(local_buffer_infos) + writes.push( + write + .dst_binding(entry.binding) + .descriptor_type(conv::map_binding_type(layout.ty)) + .buffer_info(local_buffer_infos), + ); } - vk::DescriptorType::ACCELERATION_STRUCTURE_KHR => { + wgt::BindingType::AccelerationStructure { .. } => { let start = entry.resource_index; let end = start + entry.count; @@ -1867,14 +1865,16 @@ impl crate::Device for super::Device { .acceleration_structures(local_raw_acceleration_structures), ); - write - .descriptor_count(entry.count) - .push_next(local_acceleration_structure_infos) + writes.push( + write + .dst_binding(entry.binding) + .descriptor_type(conv::map_binding_type(layout.ty)) + .descriptor_count(entry.count) + .push_next(local_acceleration_structure_infos), + ); } - _ => unreachable!(), - }; - - writes.push(write); + wgt::BindingType::ExternalTexture => unimplemented!(), + } } unsafe { self.shared.raw.update_descriptor_sets(&writes, &[]) }; diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 8a0bb03fc3..f94fed3a38 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -1005,7 +1005,8 @@ impl crate::DynSampler for Sampler {} pub struct BindGroupLayout { raw: vk::DescriptorSetLayout, desc_count: gpu_descriptor::DescriptorTotalCount, - types: Box<[(vk::DescriptorType, u32)]>, + /// Sorted list of entries. + entries: Box<[wgt::BindGroupLayoutEntry]>, /// Map of binding index to size, binding_arrays: Vec<(u32, NonZeroU32)>, } From 8a93a2fdc43a7f73c98a20efda3878bebf219100 Mon Sep 17 00:00:00 2001 From: Jamie Nicol Date: Thu, 24 Jul 2025 10:35:10 +0100 Subject: [PATCH 2/2] [vulkan, naga spv-out] Remap resource bindings In order to support external textures, we must be able to map a single external texture resource binding to multiple Vulkan descriptors. This means we must be able to override the `Binding` and `DescriptorSet` values for global variables when generating SPIR-V, rather than simply passing through the group and binding values from Naga IR. This patch extends the existing SPIR-V Naga backend's `BindingMap` to contain a descriptor set and binding value in addition to the existing array size. When creating BindGroupLayouts/BindGroups we use a sequentially incrementing value for each entry's binding value, continuing to just use the bind group index as the descriptor set value. The Naga backend looks up each resource in the map when emitting its `Binding` and `DescriptorSet` decorations. If the entry cannot be found in the map, it will either error or emit fake bindings based on its configuration. --- naga-test/src/lib.rs | 1 + naga/src/back/spv/mod.rs | 12 +++- naga/src/back/spv/writer.rs | 32 +++++++-- naga/tests/in/wgsl/binding-arrays.toml | 2 +- naga/tests/in/wgsl/binding-buffer-arrays.toml | 2 +- wgpu-hal/src/vulkan/adapter.rs | 1 + wgpu-hal/src/vulkan/device.rs | 71 +++++++++++-------- wgpu-hal/src/vulkan/mod.rs | 17 ++++- 8 files changed, 96 insertions(+), 42 deletions(-) diff --git a/naga-test/src/lib.rs b/naga-test/src/lib.rs index 6f219005c6..078aa27c40 100644 --- a/naga-test/src/lib.rs +++ b/naga-test/src/lib.rs @@ -155,6 +155,7 @@ impl SpirvOutParameters { Some(self.capabilities.clone()) }, bounds_check_policies, + fake_missing_bindings: true, binding_map: self.binding_map.clone(), zero_initialize_workgroup_memory: spv::ZeroInitializeWorkgroupMemoryMode::Polyfill, force_loop_bounding: true, diff --git a/naga/src/back/spv/mod.rs b/naga/src/back/spv/mod.rs index ab3abe9551..371b3f7dbe 100644 --- a/naga/src/back/spv/mod.rs +++ b/naga/src/back/spv/mod.rs @@ -79,6 +79,8 @@ pub enum Error { Override, #[error(transparent)] ResolveArraySizeError(#[from] crate::proc::ResolveArraySizeError), + #[error("mapping of {0:?} is missing")] + MissingBinding(crate::ResourceBinding), } #[derive(Default)] @@ -760,6 +762,7 @@ pub struct Writer { constant_ids: HandleVec, cached_constants: crate::FastHashMap, global_variables: HandleVec, + fake_missing_bindings: bool, binding_map: BindingMap, // Cached expressions are only meaningful within a BlockContext, but we @@ -811,10 +814,12 @@ bitflags::bitflags! { } } -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] pub struct BindingInfo { + pub descriptor_set: u32, + pub binding: u32, /// If the binding is an unsized binding array, this overrides the size. pub binding_array_size: Option, } @@ -839,6 +844,10 @@ pub struct Options<'a> { /// Configuration flags for the writer. pub flags: WriterFlags, + /// Don't panic on missing bindings. Instead use fake values for `Binding` + /// and `DescriptorSet` decorations. This may result in invalid SPIR-V. + pub fake_missing_bindings: bool, + /// Map of resources to information about the binding. pub binding_map: BindingMap, @@ -877,6 +886,7 @@ impl Default for Options<'_> { Options { lang_version: (1, 0), flags, + fake_missing_bindings: true, binding_map: BindingMap::default(), capabilities: None, bounds_check_policies: BoundsCheckPolicies::default(), diff --git a/naga/src/back/spv/writer.rs b/naga/src/back/spv/writer.rs index eb41421949..636766d1e5 100644 --- a/naga/src/back/spv/writer.rs +++ b/naga/src/back/spv/writer.rs @@ -87,6 +87,7 @@ impl Writer { constant_ids: HandleVec::new(), cached_constants: crate::FastHashMap::default(), global_variables: HandleVec::new(), + fake_missing_bindings: options.fake_missing_bindings, binding_map: options.binding_map.clone(), saved_cached: CachedExpressions::default(), gl450_ext_inst_id, @@ -149,6 +150,7 @@ impl Writer { force_loop_bounding: self.force_loop_bounding, use_storage_input_output_16: self.use_storage_input_output_16, capabilities_available: take(&mut self.capabilities_available), + fake_missing_bindings: self.fake_missing_bindings, binding_map: take(&mut self.binding_map), // Initialized afresh: @@ -469,6 +471,26 @@ impl Writer { }) } + /// Resolve the [`BindingInfo`] for a [`crate::ResourceBinding`] from the + /// provided [`Writer::binding_map`]. + /// + /// If the specified resource is not present in the binding map this will + /// return an error, unless [`Writer::fake_missing_bindings`] is set. + fn resolve_resource_binding( + &self, + res_binding: &crate::ResourceBinding, + ) -> Result { + match self.binding_map.get(res_binding) { + Some(target) => Ok(*target), + None if self.fake_missing_bindings => Ok(BindingInfo { + descriptor_set: res_binding.group, + binding: res_binding.binding, + binding_array_size: None, + }), + None => Err(Error::MissingBinding(*res_binding)), + } + } + /// Emits code for any wrapper functions required by the expressions in ir_function. /// The IDs of any emitted functions will be stored in [`Self::wrapped_functions`]. fn write_wrapped_functions( @@ -2241,13 +2263,11 @@ impl Writer { // and it is failing on 0. let mut substitute_inner_type_lookup = None; if let Some(ref res_binding) = global_variable.binding { - self.decorate(id, Decoration::DescriptorSet, &[res_binding.group]); - self.decorate(id, Decoration::Binding, &[res_binding.binding]); + let bind_target = self.resolve_resource_binding(res_binding)?; + self.decorate(id, Decoration::DescriptorSet, &[bind_target.descriptor_set]); + self.decorate(id, Decoration::Binding, &[bind_target.binding]); - if let Some(&BindingInfo { - binding_array_size: Some(remapped_binding_array_size), - }) = self.binding_map.get(res_binding) - { + if let Some(remapped_binding_array_size) = bind_target.binding_array_size { if let crate::TypeInner::BindingArray { base, .. } = ir_module.types[global_variable.ty].inner { diff --git a/naga/tests/in/wgsl/binding-arrays.toml b/naga/tests/in/wgsl/binding-arrays.toml index 27c1506417..a76141a672 100644 --- a/naga/tests/in/wgsl/binding-arrays.toml +++ b/naga/tests/in/wgsl/binding-arrays.toml @@ -62,5 +62,5 @@ resource_binding = { group = 0, binding = 8 } version = [1, 1] [[spv.binding_map]] -bind_target = { binding_array_size = 10 } +bind_target = { descriptor_set = 0, binding = 0, binding_array_size = 10 } resource_binding = { group = 0, binding = 0 } diff --git a/naga/tests/in/wgsl/binding-buffer-arrays.toml b/naga/tests/in/wgsl/binding-buffer-arrays.toml index be1b09cf10..7532299cb7 100644 --- a/naga/tests/in/wgsl/binding-buffer-arrays.toml +++ b/naga/tests/in/wgsl/binding-buffer-arrays.toml @@ -9,5 +9,5 @@ image = "ReadZeroSkipWrite" version = [1, 1] [[spv.binding_map]] -bind_target = { binding_array_size = 10 } +bind_target = { descriptor_set = 0, binding = 0, binding_array_size = 10 } resource_binding = { group = 0, binding = 0 } diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 0ebf1fec9a..4371c65301 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -2166,6 +2166,7 @@ impl super::Adapter { force_loop_bounding: true, use_storage_input_output_16: features.contains(wgt::Features::SHADER_F16) && self.phd_features.supports_storage_input_output_16(), + fake_missing_bindings: false, // We need to build this separately for each invocation, so just default it out here binding_map: BTreeMap::default(), debug_info: None, diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index a1a74b0230..f04bce4954 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -1482,16 +1482,17 @@ impl crate::Device for super::Device { ) -> Result { // Iterate through the entries and accumulate our Vulkan // DescriptorSetLayoutBindings and DescriptorBindingFlags, as well as - // the list of which bindings are binding arrays, and our descriptor - // counts. + // our binding map and our descriptor counts. // Note: not bothering with on stack arrays here as it's low frequency let mut vk_bindings = Vec::new(); let mut binding_flags = Vec::new(); - let mut binding_arrays = Vec::new(); + let mut binding_map = Vec::new(); + let mut next_binding = 0; + let mut contains_binding_arrays = false; let mut desc_count = gpu_descriptor::DescriptorTotalCount::default(); - for (i, entry) in desc.entries.iter().enumerate() { - if let Some(count) = entry.count { - binding_arrays.push((i as u32, count)) + for entry in desc.entries { + if entry.count.is_some() { + contains_binding_arrays = true; } let partially_bound = desc @@ -1510,7 +1511,7 @@ impl crate::Device for super::Device { wgt::BindingType::ExternalTexture => unimplemented!(), _ => { vk_bindings.push(vk::DescriptorSetLayoutBinding { - binding: entry.binding, + binding: next_binding, descriptor_type: conv::map_binding_type(entry.ty), descriptor_count: count, stage_flags: conv::map_shader_stage(entry.visibility), @@ -1518,6 +1519,14 @@ impl crate::Device for super::Device { _marker: Default::default(), }); binding_flags.push(flags); + binding_map.push(( + entry.binding, + super::BindingInfo { + binding: next_binding, + binding_array_size: entry.count, + }, + )); + next_binding += 1; } } @@ -1560,7 +1569,7 @@ impl crate::Device for super::Device { let vk_info = vk::DescriptorSetLayoutCreateInfo::default() .bindings(&vk_bindings) - .flags(if !binding_arrays.is_empty() { + .flags(if contains_binding_arrays { vk::DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL } else { vk::DescriptorSetLayoutCreateFlags::empty() @@ -1588,7 +1597,8 @@ impl crate::Device for super::Device { raw, desc_count, entries: desc.entries.into(), - binding_arrays, + binding_map, + contains_binding_arrays, }) } unsafe fn destroy_bind_group_layout(&self, bg_layout: super::BindGroupLayout) { @@ -1640,27 +1650,25 @@ impl crate::Device for super::Device { unsafe { self.shared.set_object_name(raw, label) }; } - let mut binding_arrays = BTreeMap::new(); + let mut binding_map = BTreeMap::new(); for (group, &layout) in desc.bind_group_layouts.iter().enumerate() { - for &(binding, binding_array_size) in &layout.binding_arrays { - binding_arrays.insert( + for &(binding, binding_info) in &layout.binding_map { + binding_map.insert( naga::ResourceBinding { group: group as u32, binding, }, naga::back::spv::BindingInfo { - binding_array_size: Some(binding_array_size.get()), + descriptor_set: group as u32, + binding: binding_info.binding, + binding_array_size: binding_info.binding_array_size.map(NonZeroU32::get), }, ); } } self.counters.pipeline_layouts.add(1); - - Ok(super::PipelineLayout { - raw, - binding_arrays, - }) + Ok(super::PipelineLayout { raw, binding_map }) } unsafe fn destroy_pipeline_layout(&self, pipeline_layout: super::PipelineLayout) { unsafe { @@ -1682,9 +1690,7 @@ impl crate::Device for super::Device { super::AccelerationStructure, >, ) -> Result { - let contains_binding_arrays = !desc.layout.binding_arrays.is_empty(); - - let desc_set_layout_flags = if contains_binding_arrays { + let desc_set_layout_flags = if desc.layout.contains_binding_arrays { gpu_descriptor::DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND } else { gpu_descriptor::DescriptorSetLayoutCreateFlags::empty() @@ -1780,6 +1786,7 @@ impl crate::Device for super::Device { .expect("internal error: no layout entry found with binding slot"); (layout, entry) }); + let mut next_binding = 0; for (layout, entry) in layout_and_entry_iter { let write = vk::WriteDescriptorSet::default().dst_set(*set.raw()); @@ -1794,10 +1801,11 @@ impl crate::Device for super::Device { )); writes.push( write - .dst_binding(entry.binding) + .dst_binding(next_binding) .descriptor_type(conv::map_binding_type(layout.ty)) .image_info(local_image_infos), ); + next_binding += 1; } wgt::BindingType::Texture { .. } | wgt::BindingType::StorageTexture { .. } => { let start = entry.resource_index; @@ -1815,10 +1823,11 @@ impl crate::Device for super::Device { )); writes.push( write - .dst_binding(entry.binding) + .dst_binding(next_binding) .descriptor_type(conv::map_binding_type(layout.ty)) .image_info(local_image_infos), ); + next_binding += 1; } wgt::BindingType::Buffer { .. } => { let start = entry.resource_index; @@ -1837,10 +1846,11 @@ impl crate::Device for super::Device { )); writes.push( write - .dst_binding(entry.binding) + .dst_binding(next_binding) .descriptor_type(conv::map_binding_type(layout.ty)) .buffer_info(local_buffer_infos), ); + next_binding += 1; } wgt::BindingType::AccelerationStructure { .. } => { let start = entry.resource_index; @@ -1867,11 +1877,12 @@ impl crate::Device for super::Device { writes.push( write - .dst_binding(entry.binding) + .dst_binding(next_binding) .descriptor_type(conv::map_binding_type(layout.ty)) .descriptor_count(entry.count) .push_next(local_acceleration_structure_infos), ); + next_binding += 1; } wgt::BindingType::ExternalTexture => unimplemented!(), } @@ -2033,7 +2044,7 @@ impl crate::Device for super::Device { compiled_vs = Some(self.compile_stage( vertex_stage, naga::ShaderStage::Vertex, - &desc.layout.binding_arrays, + &desc.layout.binding_map, )?); stages.push(compiled_vs.as_ref().unwrap().create_info); } @@ -2045,14 +2056,14 @@ impl crate::Device for super::Device { compiled_ts = Some(self.compile_stage( t, naga::ShaderStage::Task, - &desc.layout.binding_arrays, + &desc.layout.binding_map, )?); stages.push(compiled_ts.as_ref().unwrap().create_info); } compiled_ms = Some(self.compile_stage( mesh_stage, naga::ShaderStage::Mesh, - &desc.layout.binding_arrays, + &desc.layout.binding_map, )?); stages.push(compiled_ms.as_ref().unwrap().create_info); } @@ -2062,7 +2073,7 @@ impl crate::Device for super::Device { let compiled = self.compile_stage( stage, naga::ShaderStage::Fragment, - &desc.layout.binding_arrays, + &desc.layout.binding_map, )?; stages.push(compiled.create_info); Some(compiled) @@ -2270,7 +2281,7 @@ impl crate::Device for super::Device { let compiled = self.compile_stage( &desc.stage, naga::ShaderStage::Compute, - &desc.layout.binding_arrays, + &desc.layout.binding_map, )?; let vk_infos = [{ diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index f94fed3a38..c449f0f9c2 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -1001,14 +1001,25 @@ pub struct Sampler { impl crate::DynSampler for Sampler {} +/// Information about a binding within a specific BindGroupLayout / BindGroup. +/// This will be used to construct a [`naga::back::spv::BindingInfo`], where +/// the descriptor set value will be taken from the index of the group. +#[derive(Copy, Clone, Debug)] +struct BindingInfo { + binding: u32, + binding_array_size: Option, +} + #[derive(Debug)] pub struct BindGroupLayout { raw: vk::DescriptorSetLayout, desc_count: gpu_descriptor::DescriptorTotalCount, /// Sorted list of entries. entries: Box<[wgt::BindGroupLayoutEntry]>, - /// Map of binding index to size, - binding_arrays: Vec<(u32, NonZeroU32)>, + /// Map of original binding index to remapped binding index and optional + /// array size. + binding_map: Vec<(u32, BindingInfo)>, + contains_binding_arrays: bool, } impl crate::DynBindGroupLayout for BindGroupLayout {} @@ -1016,7 +1027,7 @@ impl crate::DynBindGroupLayout for BindGroupLayout {} #[derive(Debug)] pub struct PipelineLayout { raw: vk::PipelineLayout, - binding_arrays: naga::back::spv::BindingMap, + binding_map: naga::back::spv::BindingMap, } impl crate::DynPipelineLayout for PipelineLayout {}