Pad out binding arrays to their full length if partially bound binding arrays aren't supported. (#18126)

I mistakenly thought that with the wgpu upgrade we'd have
`PARTIALLY_BOUND_BINDING_ARRAY` everywhere. Unfortunately this is not
the case. This PR adds a workaround back in.

Closes #18098.
This commit is contained in:
Patrick Walton 2025-03-16 03:57:33 -07:00 committed by GitHub
parent 195a74afad
commit 528e68f5bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -4,7 +4,7 @@
//! allocator manages each bind group, assigning slots to materials as
//! appropriate.
use core::{marker::PhantomData, mem};
use core::{iter, marker::PhantomData, mem};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
@ -23,6 +23,7 @@ use bevy_render::{
UnpreparedBindGroup, WgpuSampler, WgpuTextureView,
},
renderer::{RenderDevice, RenderQueue},
settings::WgpuFeatures,
texture::FallbackImage,
};
use bevy_utils::default;
@ -812,6 +813,7 @@ where
&self.fallback_buffers,
fallback_image,
&self.bindless_descriptor,
self.slab_capacity,
);
}
}
@ -1171,6 +1173,7 @@ where
fallback_buffers: &HashMap<BindlessIndex, Buffer>,
fallback_image: &FallbackImage,
bindless_descriptor: &BindlessDescriptor,
slab_capacity: u32,
) {
// Create the bindless index table buffer if needed.
self.bindless_index_table.buffer.prepare(render_device);
@ -1188,6 +1191,7 @@ where
fallback_buffers,
fallback_image,
bindless_descriptor,
slab_capacity,
);
}
@ -1201,17 +1205,30 @@ where
fallback_buffers: &HashMap<BindlessIndex, Buffer>,
fallback_image: &FallbackImage,
bindless_descriptor: &BindlessDescriptor,
slab_capacity: u32,
) {
// If the bind group is clean, then do nothing.
if self.bind_group.is_some() {
return;
}
// Determine whether we need to pad out our binding arrays with dummy
// resources.
let required_binding_array_size = if render_device
.features()
.contains(WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY)
{
None
} else {
Some(slab_capacity)
};
let binding_resource_arrays = self.create_binding_resource_arrays(
fallback_bindless_resources,
fallback_buffers,
fallback_image,
bindless_descriptor,
required_binding_array_size,
);
let mut bind_group_entries = vec![BindGroupEntry {
@ -1282,6 +1299,7 @@ where
fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
fallback_image: &'a FallbackImage,
bindless_descriptor: &'a BindlessDescriptor,
required_binding_array_size: Option<u32>,
) -> Vec<(&'a u32, BindingResourceArray<'a>)> {
let mut binding_resource_arrays = vec![];
@ -1289,16 +1307,22 @@ where
self.create_sampler_binding_resource_arrays(
&mut binding_resource_arrays,
fallback_bindless_resources,
required_binding_array_size,
);
// Build texture bindings.
self.create_texture_binding_resource_arrays(&mut binding_resource_arrays, fallback_image);
self.create_texture_binding_resource_arrays(
&mut binding_resource_arrays,
fallback_image,
required_binding_array_size,
);
// Build buffer bindings.
self.create_buffer_binding_resource_arrays(
&mut binding_resource_arrays,
fallback_buffers,
bindless_descriptor,
required_binding_array_size,
);
binding_resource_arrays
@ -1310,6 +1334,7 @@ where
&'a self,
binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
fallback_bindless_resources: &'a FallbackBindlessResources,
required_binding_array_size: Option<u32>,
) {
// We have one binding resource array per sampler type.
for (bindless_resource_type, fallback_sampler) in [
@ -1326,34 +1351,41 @@ where
&fallback_bindless_resources.comparison_sampler,
),
] {
let mut sampler_bindings = vec![];
match self.samplers.get(&bindless_resource_type) {
Some(sampler_bindless_binding_array) => {
let sampler_bindings = sampler_bindless_binding_array
.bindings
.iter()
.map(|maybe_bindless_binding| match *maybe_bindless_binding {
Some(ref bindless_binding) => &bindless_binding.resource,
None => &**fallback_sampler,
})
.collect();
binding_resource_arrays.push((
&*sampler_bindless_binding_array.binding_number,
BindingResourceArray::Samplers(sampler_bindings),
));
for maybe_bindless_binding in sampler_bindless_binding_array.bindings.iter() {
match *maybe_bindless_binding {
Some(ref bindless_binding) => {
sampler_bindings.push(&*bindless_binding.resource);
}
None => sampler_bindings.push(&**fallback_sampler),
}
}
}
None => {
// Fill with a single fallback sampler.
let binding_number = bindless_resource_type
.binding_number()
.expect("Sampler bindless resource type must have a binding number");
binding_resource_arrays.push((
&**binding_number,
BindingResourceArray::Samplers(vec![&**fallback_sampler]),
));
sampler_bindings.push(&**fallback_sampler);
}
}
if let Some(required_binding_array_size) = required_binding_array_size {
sampler_bindings.extend(
iter::repeat(&**fallback_sampler)
.take(required_binding_array_size as usize - sampler_bindings.len()),
);
}
let binding_number = bindless_resource_type
.binding_number()
.expect("Sampler bindless resource type must have a binding number");
binding_resource_arrays.push((
&**binding_number,
BindingResourceArray::Samplers(sampler_bindings),
));
}
}
@ -1363,6 +1395,7 @@ where
&'a self,
binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
fallback_image: &'a FallbackImage,
required_binding_array_size: Option<u32>,
) {
for (bindless_resource_type, fallback_image) in [
(BindlessResourceType::Texture1d, &fallback_image.d1),
@ -1378,34 +1411,41 @@ where
&fallback_image.cube_array,
),
] {
let mut texture_bindings = vec![];
let binding_number = bindless_resource_type
.binding_number()
.expect("Texture bindless resource type must have a binding number");
match self.textures.get(&bindless_resource_type) {
Some(texture_bindless_binding_array) => {
let texture_bindings = texture_bindless_binding_array
.bindings
.iter()
.map(|maybe_bindless_binding| match *maybe_bindless_binding {
Some(ref bindless_binding) => &*bindless_binding.resource,
None => &*fallback_image.texture_view,
})
.collect();
binding_resource_arrays.push((
&*texture_bindless_binding_array.binding_number,
BindingResourceArray::TextureViews(texture_bindings),
));
for maybe_bindless_binding in texture_bindless_binding_array.bindings.iter() {
match *maybe_bindless_binding {
Some(ref bindless_binding) => {
texture_bindings.push(&*bindless_binding.resource);
}
None => texture_bindings.push(&*fallback_image.texture_view),
}
}
}
None => {
// Fill with a single fallback image.
let binding_number = bindless_resource_type
.binding_number()
.expect("Texture bindless resource type must have a binding number");
binding_resource_arrays.push((
binding_number,
BindingResourceArray::TextureViews(vec![&*fallback_image.texture_view]),
));
texture_bindings.push(&*fallback_image.texture_view);
}
}
if let Some(required_binding_array_size) = required_binding_array_size {
texture_bindings.extend(
iter::repeat(&*fallback_image.texture_view)
.take(required_binding_array_size as usize - texture_bindings.len()),
);
}
binding_resource_arrays.push((
binding_number,
BindingResourceArray::TextureViews(texture_bindings),
));
}
}
@ -1416,6 +1456,7 @@ where
binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
bindless_descriptor: &'a BindlessDescriptor,
required_binding_array_size: Option<u32>,
) {
for bindless_buffer_descriptor in bindless_descriptor.buffers.iter() {
let Some(buffer_bindless_binding_array) =
@ -1426,14 +1467,17 @@ where
// `BindlessDescriptor::resources`.
continue;
};
let buffer_bindings = buffer_bindless_binding_array
let fallback_buffer = fallback_buffers
.get(&bindless_buffer_descriptor.bindless_index)
.expect("Fallback buffer should exist");
let mut buffer_bindings: Vec<_> = buffer_bindless_binding_array
.bindings
.iter()
.map(|maybe_bindless_binding| {
let buffer = match *maybe_bindless_binding {
None => fallback_buffers
.get(&bindless_buffer_descriptor.bindless_index)
.expect("Fallback buffer should exist"),
None => fallback_buffer,
Some(ref bindless_binding) => &bindless_binding.resource,
};
BufferBinding {
@ -1443,6 +1487,18 @@ where
}
})
.collect();
if let Some(required_binding_array_size) = required_binding_array_size {
buffer_bindings.extend(
iter::repeat(BufferBinding {
buffer: fallback_buffer,
offset: 0,
size: None,
})
.take(required_binding_array_size as usize - buffer_bindings.len()),
);
}
binding_resource_arrays.push((
&*buffer_bindless_binding_array.binding_number,
BindingResourceArray::Buffers(buffer_bindings),