bevy_render/render_resource/
bind_group_layout_entries.rs1use bevy_utils::all_tuples_with_size;
2use core::num::NonZero;
3use wgpu::{BindGroupLayoutEntry, BindingType, ShaderStages};
4
5#[derive(Clone, Copy)]
130pub struct BindGroupLayoutEntryBuilder {
131 ty: BindingType,
132 visibility: Option<ShaderStages>,
133 count: Option<NonZero<u32>>,
134}
135
136impl BindGroupLayoutEntryBuilder {
137 pub fn visibility(mut self, visibility: ShaderStages) -> Self {
138 self.visibility = Some(visibility);
139 self
140 }
141
142 pub fn count(mut self, count: NonZero<u32>) -> Self {
143 self.count = Some(count);
144 self
145 }
146
147 pub fn build(&self, binding: u32, default_visibility: ShaderStages) -> BindGroupLayoutEntry {
148 BindGroupLayoutEntry {
149 binding,
150 ty: self.ty,
151 visibility: self.visibility.unwrap_or(default_visibility),
152 count: self.count,
153 }
154 }
155}
156
157pub struct BindGroupLayoutEntries<const N: usize> {
158 entries: [BindGroupLayoutEntry; N],
159}
160
161impl<const N: usize> BindGroupLayoutEntries<N> {
162 #[inline]
163 pub fn sequential(
164 default_visibility: ShaderStages,
165 entries_ext: impl IntoBindGroupLayoutEntryBuilderArray<N>,
166 ) -> Self {
167 let mut i = 0;
168 Self {
169 entries: entries_ext.into_array().map(|entry| {
170 let binding = i;
171 i += 1;
172 entry.build(binding, default_visibility)
173 }),
174 }
175 }
176
177 #[inline]
178 pub fn with_indices(
179 default_visibility: ShaderStages,
180 indexed_entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
181 ) -> Self {
182 Self {
183 entries: indexed_entries
184 .into_array()
185 .map(|(binding, entry)| entry.build(binding, default_visibility)),
186 }
187 }
188}
189
190impl BindGroupLayoutEntries<1> {
191 pub fn single(
192 visibility: ShaderStages,
193 resource: impl IntoBindGroupLayoutEntryBuilder,
194 ) -> [BindGroupLayoutEntry; 1] {
195 [resource
196 .into_bind_group_layout_entry_builder()
197 .build(0, visibility)]
198 }
199}
200
201impl<const N: usize> core::ops::Deref for BindGroupLayoutEntries<N> {
202 type Target = [BindGroupLayoutEntry];
203 fn deref(&self) -> &[BindGroupLayoutEntry] {
204 &self.entries
205 }
206}
207
208pub trait IntoBindGroupLayoutEntryBuilder {
209 fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder;
210}
211
212impl IntoBindGroupLayoutEntryBuilder for BindingType {
213 fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder {
214 BindGroupLayoutEntryBuilder {
215 ty: self,
216 visibility: None,
217 count: None,
218 }
219 }
220}
221
222impl IntoBindGroupLayoutEntryBuilder for BindGroupLayoutEntry {
223 fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder {
224 if self.binding != u32::MAX {
225 bevy_utils::tracing::warn!("The BindGroupLayoutEntries api ignores the binding index when converting a raw wgpu::BindGroupLayoutEntry. You can ignore this warning by setting it to u32::MAX.");
226 }
227 BindGroupLayoutEntryBuilder {
228 ty: self.ty,
229 visibility: Some(self.visibility),
230 count: self.count,
231 }
232 }
233}
234
235impl IntoBindGroupLayoutEntryBuilder for BindGroupLayoutEntryBuilder {
236 fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder {
237 self
238 }
239}
240
241pub trait IntoBindGroupLayoutEntryBuilderArray<const N: usize> {
242 fn into_array(self) -> [BindGroupLayoutEntryBuilder; N];
243}
244macro_rules! impl_to_binding_type_slice {
245 ($N: expr, $(#[$meta:meta])* $(($T: ident, $I: ident)),*) => {
246 $(#[$meta])*
247 impl<$($T: IntoBindGroupLayoutEntryBuilder),*> IntoBindGroupLayoutEntryBuilderArray<$N> for ($($T,)*) {
248 #[inline]
249 fn into_array(self) -> [BindGroupLayoutEntryBuilder; $N] {
250 let ($($I,)*) = self;
251 [$($I.into_bind_group_layout_entry_builder(), )*]
252 }
253 }
254 }
255}
256all_tuples_with_size!(
257 #[doc(fake_variadic)]
258 impl_to_binding_type_slice,
259 1,
260 32,
261 T,
262 s
263);
264
265pub trait IntoIndexedBindGroupLayoutEntryBuilderArray<const N: usize> {
266 fn into_array(self) -> [(u32, BindGroupLayoutEntryBuilder); N];
267}
268macro_rules! impl_to_indexed_binding_type_slice {
269 ($N: expr, $(($T: ident, $S: ident, $I: ident)),*) => {
270 impl<$($T: IntoBindGroupLayoutEntryBuilder),*> IntoIndexedBindGroupLayoutEntryBuilderArray<$N> for ($((u32, $T),)*) {
271 #[inline]
272 fn into_array(self) -> [(u32, BindGroupLayoutEntryBuilder); $N] {
273 let ($(($S, $I),)*) = self;
274 [$(($S, $I.into_bind_group_layout_entry_builder())), *]
275 }
276 }
277 }
278}
279all_tuples_with_size!(impl_to_indexed_binding_type_slice, 1, 32, T, n, s);
280
281impl<const N: usize> IntoBindGroupLayoutEntryBuilderArray<N> for [BindGroupLayoutEntry; N] {
282 fn into_array(self) -> [BindGroupLayoutEntryBuilder; N] {
283 self.map(IntoBindGroupLayoutEntryBuilder::into_bind_group_layout_entry_builder)
284 }
285}
286
287pub struct DynamicBindGroupLayoutEntries {
288 default_visibility: ShaderStages,
289 entries: Vec<BindGroupLayoutEntry>,
290}
291
292impl DynamicBindGroupLayoutEntries {
293 pub fn sequential<const N: usize>(
294 default_visibility: ShaderStages,
295 entries: impl IntoBindGroupLayoutEntryBuilderArray<N>,
296 ) -> Self {
297 Self {
298 default_visibility,
299 entries: entries
300 .into_array()
301 .into_iter()
302 .enumerate()
303 .map(|(ix, resource)| resource.build(ix as u32, default_visibility))
304 .collect(),
305 }
306 }
307
308 pub fn extend_sequential<const N: usize>(
309 mut self,
310 entries: impl IntoBindGroupLayoutEntryBuilderArray<N>,
311 ) -> Self {
312 let start = self.entries.last().unwrap().binding + 1;
313 self.entries.extend(
314 entries
315 .into_array()
316 .into_iter()
317 .enumerate()
318 .map(|(ix, resource)| resource.build(start + ix as u32, self.default_visibility)),
319 );
320 self
321 }
322
323 pub fn new_with_indices<const N: usize>(
324 default_visibility: ShaderStages,
325 entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
326 ) -> Self {
327 Self {
328 default_visibility,
329 entries: entries
330 .into_array()
331 .into_iter()
332 .map(|(binding, resource)| resource.build(binding, default_visibility))
333 .collect(),
334 }
335 }
336
337 pub fn extend_with_indices<const N: usize>(
338 mut self,
339 entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
340 ) -> Self {
341 self.entries.extend(
342 entries
343 .into_array()
344 .into_iter()
345 .map(|(binding, resource)| resource.build(binding, self.default_visibility)),
346 );
347 self
348 }
349}
350
351impl core::ops::Deref for DynamicBindGroupLayoutEntries {
352 type Target = [BindGroupLayoutEntry];
353
354 fn deref(&self) -> &[BindGroupLayoutEntry] {
355 &self.entries
356 }
357}
358
359pub mod binding_types {
360 use crate::render_resource::{
361 BufferBindingType, SamplerBindingType, TextureSampleType, TextureViewDimension,
362 };
363 use core::num::NonZero;
364 use encase::ShaderType;
365 use wgpu::{StorageTextureAccess, TextureFormat};
366
367 use super::*;
368
369 pub fn storage_buffer<T: ShaderType>(has_dynamic_offset: bool) -> BindGroupLayoutEntryBuilder {
370 storage_buffer_sized(has_dynamic_offset, Some(T::min_size()))
371 }
372
373 pub fn storage_buffer_sized(
374 has_dynamic_offset: bool,
375 min_binding_size: Option<NonZero<u64>>,
376 ) -> BindGroupLayoutEntryBuilder {
377 BindingType::Buffer {
378 ty: BufferBindingType::Storage { read_only: false },
379 has_dynamic_offset,
380 min_binding_size,
381 }
382 .into_bind_group_layout_entry_builder()
383 }
384
385 pub fn storage_buffer_read_only<T: ShaderType>(
386 has_dynamic_offset: bool,
387 ) -> BindGroupLayoutEntryBuilder {
388 storage_buffer_read_only_sized(has_dynamic_offset, Some(T::min_size()))
389 }
390
391 pub fn storage_buffer_read_only_sized(
392 has_dynamic_offset: bool,
393 min_binding_size: Option<NonZero<u64>>,
394 ) -> BindGroupLayoutEntryBuilder {
395 BindingType::Buffer {
396 ty: BufferBindingType::Storage { read_only: true },
397 has_dynamic_offset,
398 min_binding_size,
399 }
400 .into_bind_group_layout_entry_builder()
401 }
402
403 pub fn uniform_buffer<T: ShaderType>(has_dynamic_offset: bool) -> BindGroupLayoutEntryBuilder {
404 uniform_buffer_sized(has_dynamic_offset, Some(T::min_size()))
405 }
406
407 pub fn uniform_buffer_sized(
408 has_dynamic_offset: bool,
409 min_binding_size: Option<NonZero<u64>>,
410 ) -> BindGroupLayoutEntryBuilder {
411 BindingType::Buffer {
412 ty: BufferBindingType::Uniform,
413 has_dynamic_offset,
414 min_binding_size,
415 }
416 .into_bind_group_layout_entry_builder()
417 }
418
419 pub fn texture_1d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
420 BindingType::Texture {
421 sample_type,
422 view_dimension: TextureViewDimension::D1,
423 multisampled: false,
424 }
425 .into_bind_group_layout_entry_builder()
426 }
427
428 pub fn texture_2d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
429 BindingType::Texture {
430 sample_type,
431 view_dimension: TextureViewDimension::D2,
432 multisampled: false,
433 }
434 .into_bind_group_layout_entry_builder()
435 }
436
437 pub fn texture_2d_multisampled(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
438 BindingType::Texture {
439 sample_type,
440 view_dimension: TextureViewDimension::D2,
441 multisampled: true,
442 }
443 .into_bind_group_layout_entry_builder()
444 }
445
446 pub fn texture_2d_array(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
447 BindingType::Texture {
448 sample_type,
449 view_dimension: TextureViewDimension::D2Array,
450 multisampled: false,
451 }
452 .into_bind_group_layout_entry_builder()
453 }
454
455 pub fn texture_2d_array_multisampled(
456 sample_type: TextureSampleType,
457 ) -> BindGroupLayoutEntryBuilder {
458 BindingType::Texture {
459 sample_type,
460 view_dimension: TextureViewDimension::D2Array,
461 multisampled: true,
462 }
463 .into_bind_group_layout_entry_builder()
464 }
465
466 pub fn texture_depth_2d() -> BindGroupLayoutEntryBuilder {
467 texture_2d(TextureSampleType::Depth).into_bind_group_layout_entry_builder()
468 }
469
470 pub fn texture_depth_2d_multisampled() -> BindGroupLayoutEntryBuilder {
471 texture_2d_multisampled(TextureSampleType::Depth).into_bind_group_layout_entry_builder()
472 }
473
474 pub fn texture_cube(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
475 BindingType::Texture {
476 sample_type,
477 view_dimension: TextureViewDimension::Cube,
478 multisampled: false,
479 }
480 .into_bind_group_layout_entry_builder()
481 }
482
483 pub fn texture_cube_multisampled(
484 sample_type: TextureSampleType,
485 ) -> BindGroupLayoutEntryBuilder {
486 BindingType::Texture {
487 sample_type,
488 view_dimension: TextureViewDimension::Cube,
489 multisampled: true,
490 }
491 .into_bind_group_layout_entry_builder()
492 }
493
494 pub fn texture_cube_array(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
495 BindingType::Texture {
496 sample_type,
497 view_dimension: TextureViewDimension::CubeArray,
498 multisampled: false,
499 }
500 .into_bind_group_layout_entry_builder()
501 }
502
503 pub fn texture_cube_array_multisampled(
504 sample_type: TextureSampleType,
505 ) -> BindGroupLayoutEntryBuilder {
506 BindingType::Texture {
507 sample_type,
508 view_dimension: TextureViewDimension::CubeArray,
509 multisampled: true,
510 }
511 .into_bind_group_layout_entry_builder()
512 }
513
514 pub fn texture_3d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
515 BindingType::Texture {
516 sample_type,
517 view_dimension: TextureViewDimension::D3,
518 multisampled: false,
519 }
520 .into_bind_group_layout_entry_builder()
521 }
522
523 pub fn texture_3d_multisampled(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
524 BindingType::Texture {
525 sample_type,
526 view_dimension: TextureViewDimension::D3,
527 multisampled: true,
528 }
529 .into_bind_group_layout_entry_builder()
530 }
531
532 pub fn sampler(sampler_binding_type: SamplerBindingType) -> BindGroupLayoutEntryBuilder {
533 BindingType::Sampler(sampler_binding_type).into_bind_group_layout_entry_builder()
534 }
535
536 pub fn texture_storage_2d(
537 format: TextureFormat,
538 access: StorageTextureAccess,
539 ) -> BindGroupLayoutEntryBuilder {
540 BindingType::StorageTexture {
541 access,
542 format,
543 view_dimension: TextureViewDimension::D2,
544 }
545 .into_bind_group_layout_entry_builder()
546 }
547
548 pub fn texture_storage_2d_array(
549 format: TextureFormat,
550 access: StorageTextureAccess,
551 ) -> BindGroupLayoutEntryBuilder {
552 BindingType::StorageTexture {
553 access,
554 format,
555 view_dimension: TextureViewDimension::D2Array,
556 }
557 .into_bind_group_layout_entry_builder()
558 }
559}