bevy_render/render_resource/
bind_group_layout_entries.rs1use bevy_utils::all_tuples_with_size;
2use std::num::NonZeroU32;
3use wgpu::{BindGroupLayoutEntry, BindingType, ShaderStages};
4
5#[derive(Clone, Copy)]
130pub struct BindGroupLayoutEntryBuilder {
131 ty: BindingType,
132 visibility: Option<ShaderStages>,
133 count: Option<NonZeroU32>,
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: NonZeroU32) -> 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> std::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, $(($T: ident, $I: ident)),*) => {
246 impl<$($T: IntoBindGroupLayoutEntryBuilder),*> IntoBindGroupLayoutEntryBuilderArray<$N> for ($($T,)*) {
247 #[inline]
248 fn into_array(self) -> [BindGroupLayoutEntryBuilder; $N] {
249 let ($($I,)*) = self;
250 [$($I.into_bind_group_layout_entry_builder(), )*]
251 }
252 }
253 }
254}
255all_tuples_with_size!(impl_to_binding_type_slice, 1, 32, T, s);
256
257pub trait IntoIndexedBindGroupLayoutEntryBuilderArray<const N: usize> {
258 fn into_array(self) -> [(u32, BindGroupLayoutEntryBuilder); N];
259}
260macro_rules! impl_to_indexed_binding_type_slice {
261 ($N: expr, $(($T: ident, $S: ident, $I: ident)),*) => {
262 impl<$($T: IntoBindGroupLayoutEntryBuilder),*> IntoIndexedBindGroupLayoutEntryBuilderArray<$N> for ($((u32, $T),)*) {
263 #[inline]
264 fn into_array(self) -> [(u32, BindGroupLayoutEntryBuilder); $N] {
265 let ($(($S, $I),)*) = self;
266 [$(($S, $I.into_bind_group_layout_entry_builder())), *]
267 }
268 }
269 }
270}
271all_tuples_with_size!(impl_to_indexed_binding_type_slice, 1, 32, T, n, s);
272
273impl<const N: usize> IntoBindGroupLayoutEntryBuilderArray<N> for [BindGroupLayoutEntry; N] {
274 fn into_array(self) -> [BindGroupLayoutEntryBuilder; N] {
275 self.map(|x| x.into_bind_group_layout_entry_builder())
276 }
277}
278
279pub struct DynamicBindGroupLayoutEntries {
280 default_visibility: ShaderStages,
281 entries: Vec<BindGroupLayoutEntry>,
282}
283
284impl DynamicBindGroupLayoutEntries {
285 pub fn sequential<const N: usize>(
286 default_visibility: ShaderStages,
287 entries: impl IntoBindGroupLayoutEntryBuilderArray<N>,
288 ) -> Self {
289 Self {
290 default_visibility,
291 entries: entries
292 .into_array()
293 .into_iter()
294 .enumerate()
295 .map(|(ix, resource)| resource.build(ix as u32, default_visibility))
296 .collect(),
297 }
298 }
299
300 pub fn extend_sequential<const N: usize>(
301 mut self,
302 entries: impl IntoBindGroupLayoutEntryBuilderArray<N>,
303 ) -> Self {
304 let start = self.entries.last().unwrap().binding + 1;
305 self.entries.extend(
306 entries
307 .into_array()
308 .into_iter()
309 .enumerate()
310 .map(|(ix, resource)| resource.build(start + ix as u32, self.default_visibility)),
311 );
312 self
313 }
314
315 pub fn new_with_indices<const N: usize>(
316 default_visibility: ShaderStages,
317 entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
318 ) -> Self {
319 Self {
320 default_visibility,
321 entries: entries
322 .into_array()
323 .into_iter()
324 .map(|(binding, resource)| resource.build(binding, default_visibility))
325 .collect(),
326 }
327 }
328
329 pub fn extend_with_indices<const N: usize>(
330 mut self,
331 entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
332 ) -> Self {
333 self.entries.extend(
334 entries
335 .into_array()
336 .into_iter()
337 .map(|(binding, resource)| resource.build(binding, self.default_visibility)),
338 );
339 self
340 }
341}
342
343impl std::ops::Deref for DynamicBindGroupLayoutEntries {
344 type Target = [BindGroupLayoutEntry];
345
346 fn deref(&self) -> &[BindGroupLayoutEntry] {
347 &self.entries
348 }
349}
350
351pub mod binding_types {
352 use crate::render_resource::{
353 BufferBindingType, SamplerBindingType, TextureSampleType, TextureViewDimension,
354 };
355 use encase::ShaderType;
356 use std::num::NonZeroU64;
357 use wgpu::{StorageTextureAccess, TextureFormat};
358
359 use super::*;
360
361 pub fn storage_buffer<T: ShaderType>(has_dynamic_offset: bool) -> BindGroupLayoutEntryBuilder {
362 storage_buffer_sized(has_dynamic_offset, Some(T::min_size()))
363 }
364
365 pub fn storage_buffer_sized(
366 has_dynamic_offset: bool,
367 min_binding_size: Option<NonZeroU64>,
368 ) -> BindGroupLayoutEntryBuilder {
369 BindingType::Buffer {
370 ty: BufferBindingType::Storage { read_only: false },
371 has_dynamic_offset,
372 min_binding_size,
373 }
374 .into_bind_group_layout_entry_builder()
375 }
376
377 pub fn storage_buffer_read_only<T: ShaderType>(
378 has_dynamic_offset: bool,
379 ) -> BindGroupLayoutEntryBuilder {
380 storage_buffer_read_only_sized(has_dynamic_offset, Some(T::min_size()))
381 }
382
383 pub fn storage_buffer_read_only_sized(
384 has_dynamic_offset: bool,
385 min_binding_size: Option<NonZeroU64>,
386 ) -> BindGroupLayoutEntryBuilder {
387 BindingType::Buffer {
388 ty: BufferBindingType::Storage { read_only: true },
389 has_dynamic_offset,
390 min_binding_size,
391 }
392 .into_bind_group_layout_entry_builder()
393 }
394
395 pub fn uniform_buffer<T: ShaderType>(has_dynamic_offset: bool) -> BindGroupLayoutEntryBuilder {
396 uniform_buffer_sized(has_dynamic_offset, Some(T::min_size()))
397 }
398
399 pub fn uniform_buffer_sized(
400 has_dynamic_offset: bool,
401 min_binding_size: Option<NonZeroU64>,
402 ) -> BindGroupLayoutEntryBuilder {
403 BindingType::Buffer {
404 ty: BufferBindingType::Uniform,
405 has_dynamic_offset,
406 min_binding_size,
407 }
408 .into_bind_group_layout_entry_builder()
409 }
410
411 pub fn texture_1d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
412 BindingType::Texture {
413 sample_type,
414 view_dimension: TextureViewDimension::D1,
415 multisampled: false,
416 }
417 .into_bind_group_layout_entry_builder()
418 }
419
420 pub fn texture_2d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
421 BindingType::Texture {
422 sample_type,
423 view_dimension: TextureViewDimension::D2,
424 multisampled: false,
425 }
426 .into_bind_group_layout_entry_builder()
427 }
428
429 pub fn texture_2d_multisampled(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
430 BindingType::Texture {
431 sample_type,
432 view_dimension: TextureViewDimension::D2,
433 multisampled: true,
434 }
435 .into_bind_group_layout_entry_builder()
436 }
437
438 pub fn texture_2d_array(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
439 BindingType::Texture {
440 sample_type,
441 view_dimension: TextureViewDimension::D2Array,
442 multisampled: false,
443 }
444 .into_bind_group_layout_entry_builder()
445 }
446
447 pub fn texture_2d_array_multisampled(
448 sample_type: TextureSampleType,
449 ) -> BindGroupLayoutEntryBuilder {
450 BindingType::Texture {
451 sample_type,
452 view_dimension: TextureViewDimension::D2Array,
453 multisampled: true,
454 }
455 .into_bind_group_layout_entry_builder()
456 }
457
458 pub fn texture_depth_2d() -> BindGroupLayoutEntryBuilder {
459 texture_2d(TextureSampleType::Depth).into_bind_group_layout_entry_builder()
460 }
461
462 pub fn texture_depth_2d_multisampled() -> BindGroupLayoutEntryBuilder {
463 texture_2d_multisampled(TextureSampleType::Depth).into_bind_group_layout_entry_builder()
464 }
465
466 pub fn texture_cube(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
467 BindingType::Texture {
468 sample_type,
469 view_dimension: TextureViewDimension::Cube,
470 multisampled: false,
471 }
472 .into_bind_group_layout_entry_builder()
473 }
474
475 pub fn texture_cube_multisampled(
476 sample_type: TextureSampleType,
477 ) -> BindGroupLayoutEntryBuilder {
478 BindingType::Texture {
479 sample_type,
480 view_dimension: TextureViewDimension::Cube,
481 multisampled: true,
482 }
483 .into_bind_group_layout_entry_builder()
484 }
485
486 pub fn texture_cube_array(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
487 BindingType::Texture {
488 sample_type,
489 view_dimension: TextureViewDimension::CubeArray,
490 multisampled: false,
491 }
492 .into_bind_group_layout_entry_builder()
493 }
494
495 pub fn texture_cube_array_multisampled(
496 sample_type: TextureSampleType,
497 ) -> BindGroupLayoutEntryBuilder {
498 BindingType::Texture {
499 sample_type,
500 view_dimension: TextureViewDimension::CubeArray,
501 multisampled: true,
502 }
503 .into_bind_group_layout_entry_builder()
504 }
505
506 pub fn texture_3d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
507 BindingType::Texture {
508 sample_type,
509 view_dimension: TextureViewDimension::D3,
510 multisampled: false,
511 }
512 .into_bind_group_layout_entry_builder()
513 }
514
515 pub fn texture_3d_multisampled(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
516 BindingType::Texture {
517 sample_type,
518 view_dimension: TextureViewDimension::D3,
519 multisampled: true,
520 }
521 .into_bind_group_layout_entry_builder()
522 }
523
524 pub fn sampler(sampler_binding_type: SamplerBindingType) -> BindGroupLayoutEntryBuilder {
525 BindingType::Sampler(sampler_binding_type).into_bind_group_layout_entry_builder()
526 }
527
528 pub fn texture_storage_2d(
529 format: TextureFormat,
530 access: StorageTextureAccess,
531 ) -> BindGroupLayoutEntryBuilder {
532 BindingType::StorageTexture {
533 access,
534 format,
535 view_dimension: TextureViewDimension::D2,
536 }
537 .into_bind_group_layout_entry_builder()
538 }
539
540 pub fn texture_storage_2d_array(
541 format: TextureFormat,
542 access: StorageTextureAccess,
543 ) -> BindGroupLayoutEntryBuilder {
544 BindingType::StorageTexture {
545 access,
546 format,
547 view_dimension: TextureViewDimension::D2Array,
548 }
549 .into_bind_group_layout_entry_builder()
550 }
551}