1use core::num::NonZero;
2use variadics_please::all_tuples_with_size;
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 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 new(default_visibility: ShaderStages) -> Self {
338 Self {
339 default_visibility,
340 entries: Vec::new(),
341 }
342 }
343
344 pub fn extend_with_indices<const N: usize>(
345 mut self,
346 entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
347 ) -> Self {
348 self.entries.extend(
349 entries
350 .into_array()
351 .into_iter()
352 .map(|(binding, resource)| resource.build(binding, self.default_visibility)),
353 );
354 self
355 }
356}
357
358impl core::ops::Deref for DynamicBindGroupLayoutEntries {
359 type Target = [BindGroupLayoutEntry];
360
361 fn deref(&self) -> &[BindGroupLayoutEntry] {
362 &self.entries
363 }
364}
365
366pub mod binding_types {
367 use crate::render_resource::{
368 BufferBindingType, SamplerBindingType, TextureSampleType, TextureViewDimension,
369 };
370 use core::num::NonZero;
371 use encase::ShaderType;
372 use wgpu::{StorageTextureAccess, TextureFormat};
373
374 use super::*;
375
376 pub fn storage_buffer<T: ShaderType>(has_dynamic_offset: bool) -> BindGroupLayoutEntryBuilder {
377 storage_buffer_sized(has_dynamic_offset, Some(T::min_size()))
378 }
379
380 pub fn storage_buffer_sized(
381 has_dynamic_offset: bool,
382 min_binding_size: Option<NonZero<u64>>,
383 ) -> BindGroupLayoutEntryBuilder {
384 BindingType::Buffer {
385 ty: BufferBindingType::Storage { read_only: false },
386 has_dynamic_offset,
387 min_binding_size,
388 }
389 .into_bind_group_layout_entry_builder()
390 }
391
392 pub fn storage_buffer_read_only<T: ShaderType>(
393 has_dynamic_offset: bool,
394 ) -> BindGroupLayoutEntryBuilder {
395 storage_buffer_read_only_sized(has_dynamic_offset, Some(T::min_size()))
396 }
397
398 pub fn storage_buffer_read_only_sized(
399 has_dynamic_offset: bool,
400 min_binding_size: Option<NonZero<u64>>,
401 ) -> BindGroupLayoutEntryBuilder {
402 BindingType::Buffer {
403 ty: BufferBindingType::Storage { read_only: true },
404 has_dynamic_offset,
405 min_binding_size,
406 }
407 .into_bind_group_layout_entry_builder()
408 }
409
410 pub fn uniform_buffer<T: ShaderType>(has_dynamic_offset: bool) -> BindGroupLayoutEntryBuilder {
411 uniform_buffer_sized(has_dynamic_offset, Some(T::min_size()))
412 }
413
414 pub fn uniform_buffer_sized(
415 has_dynamic_offset: bool,
416 min_binding_size: Option<NonZero<u64>>,
417 ) -> BindGroupLayoutEntryBuilder {
418 BindingType::Buffer {
419 ty: BufferBindingType::Uniform,
420 has_dynamic_offset,
421 min_binding_size,
422 }
423 .into_bind_group_layout_entry_builder()
424 }
425
426 pub fn texture_1d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
427 BindingType::Texture {
428 sample_type,
429 view_dimension: TextureViewDimension::D1,
430 multisampled: false,
431 }
432 .into_bind_group_layout_entry_builder()
433 }
434
435 pub fn texture_2d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
436 BindingType::Texture {
437 sample_type,
438 view_dimension: TextureViewDimension::D2,
439 multisampled: false,
440 }
441 .into_bind_group_layout_entry_builder()
442 }
443
444 pub fn texture_2d_multisampled(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
445 BindingType::Texture {
446 sample_type,
447 view_dimension: TextureViewDimension::D2,
448 multisampled: true,
449 }
450 .into_bind_group_layout_entry_builder()
451 }
452
453 pub fn texture_2d_array(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
454 BindingType::Texture {
455 sample_type,
456 view_dimension: TextureViewDimension::D2Array,
457 multisampled: false,
458 }
459 .into_bind_group_layout_entry_builder()
460 }
461
462 pub fn texture_2d_array_multisampled(
463 sample_type: TextureSampleType,
464 ) -> BindGroupLayoutEntryBuilder {
465 BindingType::Texture {
466 sample_type,
467 view_dimension: TextureViewDimension::D2Array,
468 multisampled: true,
469 }
470 .into_bind_group_layout_entry_builder()
471 }
472
473 pub fn texture_depth_2d() -> BindGroupLayoutEntryBuilder {
474 texture_2d(TextureSampleType::Depth).into_bind_group_layout_entry_builder()
475 }
476
477 pub fn texture_depth_2d_multisampled() -> BindGroupLayoutEntryBuilder {
478 texture_2d_multisampled(TextureSampleType::Depth).into_bind_group_layout_entry_builder()
479 }
480
481 pub fn texture_cube(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
482 BindingType::Texture {
483 sample_type,
484 view_dimension: TextureViewDimension::Cube,
485 multisampled: false,
486 }
487 .into_bind_group_layout_entry_builder()
488 }
489
490 pub fn texture_cube_multisampled(
491 sample_type: TextureSampleType,
492 ) -> BindGroupLayoutEntryBuilder {
493 BindingType::Texture {
494 sample_type,
495 view_dimension: TextureViewDimension::Cube,
496 multisampled: true,
497 }
498 .into_bind_group_layout_entry_builder()
499 }
500
501 pub fn texture_cube_array(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
502 BindingType::Texture {
503 sample_type,
504 view_dimension: TextureViewDimension::CubeArray,
505 multisampled: false,
506 }
507 .into_bind_group_layout_entry_builder()
508 }
509
510 pub fn texture_cube_array_multisampled(
511 sample_type: TextureSampleType,
512 ) -> BindGroupLayoutEntryBuilder {
513 BindingType::Texture {
514 sample_type,
515 view_dimension: TextureViewDimension::CubeArray,
516 multisampled: true,
517 }
518 .into_bind_group_layout_entry_builder()
519 }
520
521 pub fn texture_3d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
522 BindingType::Texture {
523 sample_type,
524 view_dimension: TextureViewDimension::D3,
525 multisampled: false,
526 }
527 .into_bind_group_layout_entry_builder()
528 }
529
530 pub fn texture_3d_multisampled(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
531 BindingType::Texture {
532 sample_type,
533 view_dimension: TextureViewDimension::D3,
534 multisampled: true,
535 }
536 .into_bind_group_layout_entry_builder()
537 }
538
539 pub fn sampler(sampler_binding_type: SamplerBindingType) -> BindGroupLayoutEntryBuilder {
540 BindingType::Sampler(sampler_binding_type).into_bind_group_layout_entry_builder()
541 }
542
543 pub fn texture_storage_2d(
544 format: TextureFormat,
545 access: StorageTextureAccess,
546 ) -> BindGroupLayoutEntryBuilder {
547 BindingType::StorageTexture {
548 access,
549 format,
550 view_dimension: TextureViewDimension::D2,
551 }
552 .into_bind_group_layout_entry_builder()
553 }
554
555 pub fn texture_storage_2d_array(
556 format: TextureFormat,
557 access: StorageTextureAccess,
558 ) -> BindGroupLayoutEntryBuilder {
559 BindingType::StorageTexture {
560 access,
561 format,
562 view_dimension: TextureViewDimension::D2Array,
563 }
564 .into_bind_group_layout_entry_builder()
565 }
566
567 pub fn texture_storage_3d(
568 format: TextureFormat,
569 access: StorageTextureAccess,
570 ) -> BindGroupLayoutEntryBuilder {
571 BindingType::StorageTexture {
572 access,
573 format,
574 view_dimension: TextureViewDimension::D3,
575 }
576 .into_bind_group_layout_entry_builder()
577 }
578
579 pub fn acceleration_structure() -> BindGroupLayoutEntryBuilder {
580 BindingType::AccelerationStructure {
581 vertex_return: false,
582 }
583 .into_bind_group_layout_entry_builder()
584 }
585
586 pub fn acceleration_structure_vertex_return() -> BindGroupLayoutEntryBuilder {
587 BindingType::AccelerationStructure {
588 vertex_return: true,
589 }
590 .into_bind_group_layout_entry_builder()
591 }
592}