1
+ use alloc:: borrow:: Cow ;
2
+
1
3
use bevy_asset:: { Asset , Handle } ;
2
4
use bevy_ecs:: system:: SystemParamItem ;
5
+ use bevy_platform_support:: { collections:: HashSet , hash:: FixedHasher } ;
3
6
use bevy_reflect:: { impl_type_path, Reflect } ;
4
7
use bevy_render:: {
5
8
alpha:: AlphaMode ,
6
9
mesh:: MeshVertexBufferLayoutRef ,
7
10
render_resource:: {
8
- AsBindGroup , AsBindGroupError , BindGroupLayout , BindlessDescriptor ,
9
- BindlessSlabResourceLimit , RenderPipelineDescriptor , Shader , ShaderRef ,
10
- SpecializedMeshPipelineError , UnpreparedBindGroup ,
11
+ AsBindGroup , AsBindGroupError , BindGroupLayout , BindGroupLayoutEntry , BindlessDescriptor ,
12
+ BindlessResourceType , BindlessSlabResourceLimit , RenderPipelineDescriptor , Shader ,
13
+ ShaderRef , SpecializedMeshPipelineError , UnpreparedBindGroup ,
11
14
} ,
12
15
renderer:: RenderDevice ,
13
16
} ;
@@ -156,27 +159,53 @@ impl<B: Material, E: MaterialExtension> AsBindGroup for ExtendedMaterial<B, E> {
156
159
type Param = ( <B as AsBindGroup >:: Param , <E as AsBindGroup >:: Param ) ;
157
160
158
161
fn bindless_slot_count ( ) -> Option < BindlessSlabResourceLimit > {
159
- // For now, disable bindless in `ExtendedMaterial`.
160
- if B :: bindless_slot_count ( ) . is_some ( ) && E :: bindless_slot_count ( ) . is_some ( ) {
161
- panic ! ( "Bindless extended materials are currently unsupported" )
162
+ // We only enable bindless if both the base material and its extension
163
+ // are bindless. If we do enable bindless, we choose the smaller of the
164
+ // two slab size limits.
165
+ match ( B :: bindless_slot_count ( ) ?, E :: bindless_slot_count ( ) ?) {
166
+ ( BindlessSlabResourceLimit :: Auto , BindlessSlabResourceLimit :: Auto ) => {
167
+ Some ( BindlessSlabResourceLimit :: Auto )
168
+ }
169
+ ( BindlessSlabResourceLimit :: Auto , BindlessSlabResourceLimit :: Custom ( limit) )
170
+ | ( BindlessSlabResourceLimit :: Custom ( limit) , BindlessSlabResourceLimit :: Auto ) => {
171
+ Some ( BindlessSlabResourceLimit :: Custom ( limit) )
172
+ }
173
+ (
174
+ BindlessSlabResourceLimit :: Custom ( base_limit) ,
175
+ BindlessSlabResourceLimit :: Custom ( extended_limit) ,
176
+ ) => Some ( BindlessSlabResourceLimit :: Custom (
177
+ base_limit. min ( extended_limit) ,
178
+ ) ) ,
162
179
}
163
- None
164
180
}
165
181
166
182
fn unprepared_bind_group (
167
183
& self ,
168
184
layout : & BindGroupLayout ,
169
185
render_device : & RenderDevice ,
170
186
( base_param, extended_param) : & mut SystemParamItem < ' _ , ' _ , Self :: Param > ,
171
- _ : bool ,
187
+ mut force_non_bindless : bool ,
172
188
) -> Result < UnpreparedBindGroup < Self :: Data > , AsBindGroupError > {
189
+ force_non_bindless = force_non_bindless || Self :: bindless_slot_count ( ) . is_none ( ) ;
190
+
173
191
// add together the bindings of the base material and the user material
174
192
let UnpreparedBindGroup {
175
193
mut bindings,
176
194
data : base_data,
177
- } = B :: unprepared_bind_group ( & self . base , layout, render_device, base_param, true ) ?;
178
- let extended_bindgroup =
179
- E :: unprepared_bind_group ( & self . extension , layout, render_device, extended_param, true ) ?;
195
+ } = B :: unprepared_bind_group (
196
+ & self . base ,
197
+ layout,
198
+ render_device,
199
+ base_param,
200
+ force_non_bindless,
201
+ ) ?;
202
+ let extended_bindgroup = E :: unprepared_bind_group (
203
+ & self . extension ,
204
+ layout,
205
+ render_device,
206
+ extended_param,
207
+ force_non_bindless,
208
+ ) ?;
180
209
181
210
bindings. extend ( extended_bindgroup. bindings . 0 ) ;
182
211
@@ -188,23 +217,72 @@ impl<B: Material, E: MaterialExtension> AsBindGroup for ExtendedMaterial<B, E> {
188
217
189
218
fn bind_group_layout_entries (
190
219
render_device : & RenderDevice ,
191
- _ : bool ,
192
- ) -> Vec < bevy_render :: render_resource :: BindGroupLayoutEntry >
220
+ mut force_non_bindless : bool ,
221
+ ) -> Vec < BindGroupLayoutEntry >
193
222
where
194
223
Self : Sized ,
195
224
{
196
- // add together the bindings of the standard material and the user material
197
- let mut entries = B :: bind_group_layout_entries ( render_device, true ) ;
198
- entries. extend ( E :: bind_group_layout_entries ( render_device, true ) ) ;
225
+ force_non_bindless = force_non_bindless || Self :: bindless_slot_count ( ) . is_none ( ) ;
226
+
227
+ // Add together the bindings of the standard material and the user
228
+ // material, skipping duplicate bindings. Duplicate bindings will occur
229
+ // when bindless mode is on, because of the common bindless resource
230
+ // arrays, and we need to eliminate the duplicates or `wgpu` will
231
+ // complain.
232
+ let mut entries = vec ! [ ] ;
233
+ let mut seen_bindings = HashSet :: < _ > :: with_hasher ( FixedHasher ) ;
234
+ for entry in B :: bind_group_layout_entries ( render_device, force_non_bindless)
235
+ . into_iter ( )
236
+ . chain ( E :: bind_group_layout_entries ( render_device, force_non_bindless) . into_iter ( ) )
237
+ {
238
+ if seen_bindings. insert ( entry. binding ) {
239
+ entries. push ( entry) ;
240
+ }
241
+ }
199
242
entries
200
243
}
201
244
202
245
fn bindless_descriptor ( ) -> Option < BindlessDescriptor > {
203
- if B :: bindless_descriptor ( ) . is_some ( ) && E :: bindless_descriptor ( ) . is_some ( ) {
204
- panic ! ( "Bindless extended materials are currently unsupported" )
246
+ // We're going to combine the two bindless descriptors.
247
+ let base_bindless_descriptor = B :: bindless_descriptor ( ) ?;
248
+ let extended_bindless_descriptor = E :: bindless_descriptor ( ) ?;
249
+
250
+ // Combining the buffers and index tables is straightforward.
251
+
252
+ let mut buffers = base_bindless_descriptor. buffers . to_vec ( ) ;
253
+ let mut index_tables = base_bindless_descriptor. index_tables . to_vec ( ) ;
254
+
255
+ buffers. extend ( extended_bindless_descriptor. buffers . iter ( ) . cloned ( ) ) ;
256
+ index_tables. extend ( extended_bindless_descriptor. index_tables . iter ( ) . cloned ( ) ) ;
257
+
258
+ // Combining the resources is a little trickier because the resource
259
+ // array is indexed by bindless index, so we have to merge the two
260
+ // arrays, not just concatenate them.
261
+ let max_bindless_index = base_bindless_descriptor
262
+ . resources
263
+ . len ( )
264
+ . max ( extended_bindless_descriptor. resources . len ( ) ) ;
265
+ let mut resources = Vec :: with_capacity ( max_bindless_index) ;
266
+ for bindless_index in 0 ..max_bindless_index {
267
+ // In the event of a conflicting bindless index, we choose the
268
+ // base's binding.
269
+ match base_bindless_descriptor. resources . get ( bindless_index) {
270
+ None | Some ( & BindlessResourceType :: None ) => resources. push (
271
+ extended_bindless_descriptor
272
+ . resources
273
+ . get ( bindless_index)
274
+ . copied ( )
275
+ . unwrap_or ( BindlessResourceType :: None ) ,
276
+ ) ,
277
+ Some ( & resource_type) => resources. push ( resource_type) ,
278
+ }
205
279
}
206
280
207
- None
281
+ Some ( BindlessDescriptor {
282
+ resources : Cow :: Owned ( resources) ,
283
+ buffers : Cow :: Owned ( buffers) ,
284
+ index_tables : Cow :: Owned ( index_tables) ,
285
+ } )
208
286
}
209
287
}
210
288
0 commit comments