bevy_pbr/render/
mesh_bindings.rs

1//! Bind group layout related definitions for the mesh pipeline.
2
3use bevy_math::Mat4;
4use bevy_mesh::morph::MAX_MORPH_WEIGHTS;
5use bevy_render::{
6    render_resource::*,
7    renderer::{RenderAdapter, RenderDevice},
8};
9
10use crate::{binding_arrays_are_usable, render::skin::MAX_JOINTS, LightmapSlab};
11
12const MORPH_WEIGHT_SIZE: usize = size_of::<f32>();
13
14/// This is used to allocate buffers.
15/// The correctness of the value depends on the GPU/platform.
16/// The current value is chosen because it is guaranteed to work everywhere.
17/// To allow for bigger values, a check must be made for the limits
18/// of the GPU at runtime, which would mean not using consts anymore.
19pub const MORPH_BUFFER_SIZE: usize = MAX_MORPH_WEIGHTS * MORPH_WEIGHT_SIZE;
20
21const JOINT_SIZE: usize = size_of::<Mat4>();
22pub(crate) const JOINT_BUFFER_SIZE: usize = MAX_JOINTS * JOINT_SIZE;
23
24/// Individual layout entries.
25mod layout_entry {
26    use core::num::NonZeroU32;
27
28    use super::{JOINT_BUFFER_SIZE, MORPH_BUFFER_SIZE};
29    use crate::{render::skin, MeshUniform, LIGHTMAPS_PER_SLAB};
30    use bevy_render::{
31        render_resource::{
32            binding_types::{
33                sampler, storage_buffer_read_only_sized, texture_2d, texture_3d,
34                uniform_buffer_sized,
35            },
36            BindGroupLayoutEntryBuilder, BufferSize, GpuArrayBuffer, SamplerBindingType,
37            ShaderStages, TextureSampleType,
38        },
39        settings::WgpuLimits,
40    };
41
42    pub(super) fn model(limits: &WgpuLimits) -> BindGroupLayoutEntryBuilder {
43        GpuArrayBuffer::<MeshUniform>::binding_layout(limits)
44            .visibility(ShaderStages::VERTEX_FRAGMENT)
45    }
46    pub(super) fn skinning(limits: &WgpuLimits) -> BindGroupLayoutEntryBuilder {
47        // If we can use storage buffers, do so. Otherwise, fall back to uniform
48        // buffers.
49        let size = BufferSize::new(JOINT_BUFFER_SIZE as u64);
50        if skin::skins_use_uniform_buffers(limits) {
51            uniform_buffer_sized(true, size)
52        } else {
53            storage_buffer_read_only_sized(false, size)
54        }
55    }
56    pub(super) fn weights() -> BindGroupLayoutEntryBuilder {
57        uniform_buffer_sized(true, BufferSize::new(MORPH_BUFFER_SIZE as u64))
58    }
59    pub(super) fn targets() -> BindGroupLayoutEntryBuilder {
60        texture_3d(TextureSampleType::Float { filterable: false })
61    }
62    pub(super) fn lightmaps_texture_view() -> BindGroupLayoutEntryBuilder {
63        texture_2d(TextureSampleType::Float { filterable: true }).visibility(ShaderStages::FRAGMENT)
64    }
65    pub(super) fn lightmaps_sampler() -> BindGroupLayoutEntryBuilder {
66        sampler(SamplerBindingType::Filtering).visibility(ShaderStages::FRAGMENT)
67    }
68    pub(super) fn lightmaps_texture_view_array() -> BindGroupLayoutEntryBuilder {
69        texture_2d(TextureSampleType::Float { filterable: true })
70            .visibility(ShaderStages::FRAGMENT)
71            .count(NonZeroU32::new(LIGHTMAPS_PER_SLAB as u32).unwrap())
72    }
73    pub(super) fn lightmaps_sampler_array() -> BindGroupLayoutEntryBuilder {
74        sampler(SamplerBindingType::Filtering)
75            .visibility(ShaderStages::FRAGMENT)
76            .count(NonZeroU32::new(LIGHTMAPS_PER_SLAB as u32).unwrap())
77    }
78}
79
80/// Individual [`BindGroupEntry`]
81/// for bind groups.
82mod entry {
83    use crate::render::skin;
84
85    use super::{JOINT_BUFFER_SIZE, MORPH_BUFFER_SIZE};
86    use bevy_render::{
87        render_resource::{
88            BindGroupEntry, BindingResource, Buffer, BufferBinding, BufferSize, Sampler,
89            TextureView, WgpuSampler, WgpuTextureView,
90        },
91        renderer::RenderDevice,
92    };
93
94    fn entry(binding: u32, size: Option<u64>, buffer: &Buffer) -> BindGroupEntry<'_> {
95        BindGroupEntry {
96            binding,
97            resource: BindingResource::Buffer(BufferBinding {
98                buffer,
99                offset: 0,
100                size: size.map(|size| BufferSize::new(size).unwrap()),
101            }),
102        }
103    }
104    pub(super) fn model(binding: u32, resource: BindingResource) -> BindGroupEntry {
105        BindGroupEntry { binding, resource }
106    }
107    pub(super) fn skinning<'a>(
108        render_device: &RenderDevice,
109        binding: u32,
110        buffer: &'a Buffer,
111    ) -> BindGroupEntry<'a> {
112        let size = if skin::skins_use_uniform_buffers(&render_device.limits()) {
113            Some(JOINT_BUFFER_SIZE as u64)
114        } else {
115            None
116        };
117        entry(binding, size, buffer)
118    }
119    pub(super) fn weights(binding: u32, buffer: &Buffer) -> BindGroupEntry<'_> {
120        entry(binding, Some(MORPH_BUFFER_SIZE as u64), buffer)
121    }
122    pub(super) fn targets(binding: u32, texture: &TextureView) -> BindGroupEntry<'_> {
123        BindGroupEntry {
124            binding,
125            resource: BindingResource::TextureView(texture),
126        }
127    }
128    pub(super) fn lightmaps_texture_view(
129        binding: u32,
130        texture: &TextureView,
131    ) -> BindGroupEntry<'_> {
132        BindGroupEntry {
133            binding,
134            resource: BindingResource::TextureView(texture),
135        }
136    }
137    pub(super) fn lightmaps_sampler(binding: u32, sampler: &Sampler) -> BindGroupEntry<'_> {
138        BindGroupEntry {
139            binding,
140            resource: BindingResource::Sampler(sampler),
141        }
142    }
143    pub(super) fn lightmaps_texture_view_array<'a>(
144        binding: u32,
145        textures: &'a [&'a WgpuTextureView],
146    ) -> BindGroupEntry<'a> {
147        BindGroupEntry {
148            binding,
149            resource: BindingResource::TextureViewArray(textures),
150        }
151    }
152    pub(super) fn lightmaps_sampler_array<'a>(
153        binding: u32,
154        samplers: &'a [&'a WgpuSampler],
155    ) -> BindGroupEntry<'a> {
156        BindGroupEntry {
157            binding,
158            resource: BindingResource::SamplerArray(samplers),
159        }
160    }
161}
162
163/// All possible [`BindGroupLayout`]s in bevy's default mesh shader (`mesh.wgsl`).
164#[derive(Clone)]
165pub struct MeshLayouts {
166    /// The mesh model uniform (transform) and nothing else.
167    pub model_only: BindGroupLayoutDescriptor,
168
169    /// Includes the lightmap texture and uniform.
170    pub lightmapped: BindGroupLayoutDescriptor,
171
172    /// Also includes the uniform for skinning
173    pub skinned: BindGroupLayoutDescriptor,
174
175    /// Like [`MeshLayouts::skinned`], but includes slots for the previous
176    /// frame's joint matrices, so that we can compute motion vectors.
177    pub skinned_motion: BindGroupLayoutDescriptor,
178
179    /// Also includes the uniform and [`MorphAttributes`] for morph targets.
180    ///
181    /// [`MorphAttributes`]: bevy_mesh::morph::MorphAttributes
182    pub morphed: BindGroupLayoutDescriptor,
183
184    /// Like [`MeshLayouts::morphed`], but includes a slot for the previous
185    /// frame's morph weights, so that we can compute motion vectors.
186    pub morphed_motion: BindGroupLayoutDescriptor,
187
188    /// Also includes both uniforms for skinning and morph targets, also the
189    /// morph target [`MorphAttributes`] binding.
190    ///
191    /// [`MorphAttributes`]: bevy_mesh::morph::MorphAttributes
192    pub morphed_skinned: BindGroupLayoutDescriptor,
193
194    /// Like [`MeshLayouts::morphed_skinned`], but includes slots for the
195    /// previous frame's joint matrices and morph weights, so that we can
196    /// compute motion vectors.
197    pub morphed_skinned_motion: BindGroupLayoutDescriptor,
198}
199
200impl MeshLayouts {
201    /// Prepare the layouts used by the default bevy [`Mesh`].
202    ///
203    /// [`Mesh`]: bevy_mesh::Mesh
204    pub fn new(render_device: &RenderDevice, render_adapter: &RenderAdapter) -> Self {
205        MeshLayouts {
206            model_only: Self::model_only_layout(render_device),
207            lightmapped: Self::lightmapped_layout(render_device, render_adapter),
208            skinned: Self::skinned_layout(render_device),
209            skinned_motion: Self::skinned_motion_layout(render_device),
210            morphed: Self::morphed_layout(render_device),
211            morphed_motion: Self::morphed_motion_layout(render_device),
212            morphed_skinned: Self::morphed_skinned_layout(render_device),
213            morphed_skinned_motion: Self::morphed_skinned_motion_layout(render_device),
214        }
215    }
216
217    // ---------- create individual BindGroupLayouts ----------
218
219    fn model_only_layout(render_device: &RenderDevice) -> BindGroupLayoutDescriptor {
220        BindGroupLayoutDescriptor::new(
221            "mesh_layout",
222            &BindGroupLayoutEntries::single(
223                ShaderStages::empty(),
224                layout_entry::model(&render_device.limits()),
225            ),
226        )
227    }
228
229    /// Creates the layout for skinned meshes.
230    fn skinned_layout(render_device: &RenderDevice) -> BindGroupLayoutDescriptor {
231        BindGroupLayoutDescriptor::new(
232            "skinned_mesh_layout",
233            &BindGroupLayoutEntries::with_indices(
234                ShaderStages::VERTEX,
235                (
236                    (0, layout_entry::model(&render_device.limits())),
237                    // The current frame's joint matrix buffer.
238                    (1, layout_entry::skinning(&render_device.limits())),
239                ),
240            ),
241        )
242    }
243
244    /// Creates the layout for skinned meshes with the infrastructure to compute
245    /// motion vectors.
246    fn skinned_motion_layout(render_device: &RenderDevice) -> BindGroupLayoutDescriptor {
247        BindGroupLayoutDescriptor::new(
248            "skinned_motion_mesh_layout",
249            &BindGroupLayoutEntries::with_indices(
250                ShaderStages::VERTEX,
251                (
252                    (0, layout_entry::model(&render_device.limits())),
253                    // The current frame's joint matrix buffer.
254                    (1, layout_entry::skinning(&render_device.limits())),
255                    // The previous frame's joint matrix buffer.
256                    (6, layout_entry::skinning(&render_device.limits())),
257                ),
258            ),
259        )
260    }
261
262    /// Creates the layout for meshes with morph targets.
263    fn morphed_layout(render_device: &RenderDevice) -> BindGroupLayoutDescriptor {
264        BindGroupLayoutDescriptor::new(
265            "morphed_mesh_layout",
266            &BindGroupLayoutEntries::with_indices(
267                ShaderStages::VERTEX,
268                (
269                    (0, layout_entry::model(&render_device.limits())),
270                    // The current frame's morph weight buffer.
271                    (2, layout_entry::weights()),
272                    (3, layout_entry::targets()),
273                ),
274            ),
275        )
276    }
277
278    /// Creates the layout for meshes with morph targets and the infrastructure
279    /// to compute motion vectors.
280    fn morphed_motion_layout(render_device: &RenderDevice) -> BindGroupLayoutDescriptor {
281        BindGroupLayoutDescriptor::new(
282            "morphed_mesh_layout",
283            &BindGroupLayoutEntries::with_indices(
284                ShaderStages::VERTEX,
285                (
286                    (0, layout_entry::model(&render_device.limits())),
287                    // The current frame's morph weight buffer.
288                    (2, layout_entry::weights()),
289                    (3, layout_entry::targets()),
290                    // The previous frame's morph weight buffer.
291                    (7, layout_entry::weights()),
292                ),
293            ),
294        )
295    }
296
297    /// Creates the bind group layout for meshes with both skins and morph
298    /// targets.
299    fn morphed_skinned_layout(render_device: &RenderDevice) -> BindGroupLayoutDescriptor {
300        BindGroupLayoutDescriptor::new(
301            "morphed_skinned_mesh_layout",
302            &BindGroupLayoutEntries::with_indices(
303                ShaderStages::VERTEX,
304                (
305                    (0, layout_entry::model(&render_device.limits())),
306                    // The current frame's joint matrix buffer.
307                    (1, layout_entry::skinning(&render_device.limits())),
308                    // The current frame's morph weight buffer.
309                    (2, layout_entry::weights()),
310                    (3, layout_entry::targets()),
311                ),
312            ),
313        )
314    }
315
316    /// Creates the bind group layout for meshes with both skins and morph
317    /// targets, in addition to the infrastructure to compute motion vectors.
318    fn morphed_skinned_motion_layout(render_device: &RenderDevice) -> BindGroupLayoutDescriptor {
319        BindGroupLayoutDescriptor::new(
320            "morphed_skinned_motion_mesh_layout",
321            &BindGroupLayoutEntries::with_indices(
322                ShaderStages::VERTEX,
323                (
324                    (0, layout_entry::model(&render_device.limits())),
325                    // The current frame's joint matrix buffer.
326                    (1, layout_entry::skinning(&render_device.limits())),
327                    // The current frame's morph weight buffer.
328                    (2, layout_entry::weights()),
329                    (3, layout_entry::targets()),
330                    // The previous frame's joint matrix buffer.
331                    (6, layout_entry::skinning(&render_device.limits())),
332                    // The previous frame's morph weight buffer.
333                    (7, layout_entry::weights()),
334                ),
335            ),
336        )
337    }
338
339    fn lightmapped_layout(
340        render_device: &RenderDevice,
341        render_adapter: &RenderAdapter,
342    ) -> BindGroupLayoutDescriptor {
343        if binding_arrays_are_usable(render_device, render_adapter) {
344            BindGroupLayoutDescriptor::new(
345                "lightmapped_mesh_layout",
346                &BindGroupLayoutEntries::with_indices(
347                    ShaderStages::VERTEX,
348                    (
349                        (0, layout_entry::model(&render_device.limits())),
350                        (4, layout_entry::lightmaps_texture_view_array()),
351                        (5, layout_entry::lightmaps_sampler_array()),
352                    ),
353                ),
354            )
355        } else {
356            BindGroupLayoutDescriptor::new(
357                "lightmapped_mesh_layout",
358                &BindGroupLayoutEntries::with_indices(
359                    ShaderStages::VERTEX,
360                    (
361                        (0, layout_entry::model(&render_device.limits())),
362                        (4, layout_entry::lightmaps_texture_view()),
363                        (5, layout_entry::lightmaps_sampler()),
364                    ),
365                ),
366            )
367        }
368    }
369
370    // ---------- BindGroup methods ----------
371
372    pub fn model_only(
373        &self,
374        render_device: &RenderDevice,
375        pipeline_cache: &PipelineCache,
376        model: &BindingResource,
377    ) -> BindGroup {
378        render_device.create_bind_group(
379            "model_only_mesh_bind_group",
380            &pipeline_cache.get_bind_group_layout(&self.model_only),
381            &[entry::model(0, model.clone())],
382        )
383    }
384
385    pub fn lightmapped(
386        &self,
387        render_device: &RenderDevice,
388        pipeline_cache: &PipelineCache,
389        model: &BindingResource,
390        lightmap_slab: &LightmapSlab,
391        bindless_lightmaps: bool,
392    ) -> BindGroup {
393        if bindless_lightmaps {
394            let (texture_views, samplers) = lightmap_slab.build_binding_arrays();
395            render_device.create_bind_group(
396                "lightmapped_mesh_bind_group",
397                &pipeline_cache.get_bind_group_layout(&self.lightmapped),
398                &[
399                    entry::model(0, model.clone()),
400                    entry::lightmaps_texture_view_array(4, &texture_views),
401                    entry::lightmaps_sampler_array(5, &samplers),
402                ],
403            )
404        } else {
405            let (texture_view, sampler) = lightmap_slab.bindings_for_first_lightmap();
406            render_device.create_bind_group(
407                "lightmapped_mesh_bind_group",
408                &pipeline_cache.get_bind_group_layout(&self.lightmapped),
409                &[
410                    entry::model(0, model.clone()),
411                    entry::lightmaps_texture_view(4, texture_view),
412                    entry::lightmaps_sampler(5, sampler),
413                ],
414            )
415        }
416    }
417
418    /// Creates the bind group for skinned meshes with no morph targets.
419    pub fn skinned(
420        &self,
421        render_device: &RenderDevice,
422        pipeline_cache: &PipelineCache,
423        model: &BindingResource,
424        current_skin: &Buffer,
425    ) -> BindGroup {
426        render_device.create_bind_group(
427            "skinned_mesh_bind_group",
428            &pipeline_cache.get_bind_group_layout(&self.skinned),
429            &[
430                entry::model(0, model.clone()),
431                entry::skinning(render_device, 1, current_skin),
432            ],
433        )
434    }
435
436    /// Creates the bind group for skinned meshes with no morph targets, with
437    /// the infrastructure to compute motion vectors.
438    ///
439    /// `current_skin` is the buffer of joint matrices for this frame;
440    /// `prev_skin` is the buffer for the previous frame. The latter is used for
441    /// motion vector computation. If there is no such applicable buffer,
442    /// `current_skin` and `prev_skin` will reference the same buffer.
443    pub fn skinned_motion(
444        &self,
445        render_device: &RenderDevice,
446        pipeline_cache: &PipelineCache,
447        model: &BindingResource,
448        current_skin: &Buffer,
449        prev_skin: &Buffer,
450    ) -> BindGroup {
451        render_device.create_bind_group(
452            "skinned_motion_mesh_bind_group",
453            &pipeline_cache.get_bind_group_layout(&self.skinned_motion),
454            &[
455                entry::model(0, model.clone()),
456                entry::skinning(render_device, 1, current_skin),
457                entry::skinning(render_device, 6, prev_skin),
458            ],
459        )
460    }
461
462    /// Creates the bind group for meshes with no skins but morph targets.
463    pub fn morphed(
464        &self,
465        render_device: &RenderDevice,
466        pipeline_cache: &PipelineCache,
467        model: &BindingResource,
468        current_weights: &Buffer,
469        targets: &TextureView,
470    ) -> BindGroup {
471        render_device.create_bind_group(
472            "morphed_mesh_bind_group",
473            &pipeline_cache.get_bind_group_layout(&self.morphed),
474            &[
475                entry::model(0, model.clone()),
476                entry::weights(2, current_weights),
477                entry::targets(3, targets),
478            ],
479        )
480    }
481
482    /// Creates the bind group for meshes with no skins but morph targets, in
483    /// addition to the infrastructure to compute motion vectors.
484    ///
485    /// `current_weights` is the buffer of morph weights for this frame;
486    /// `prev_weights` is the buffer for the previous frame. The latter is used
487    /// for motion vector computation. If there is no such applicable buffer,
488    /// `current_weights` and `prev_weights` will reference the same buffer.
489    pub fn morphed_motion(
490        &self,
491        render_device: &RenderDevice,
492        pipeline_cache: &PipelineCache,
493        model: &BindingResource,
494        current_weights: &Buffer,
495        targets: &TextureView,
496        prev_weights: &Buffer,
497    ) -> BindGroup {
498        render_device.create_bind_group(
499            "morphed_motion_mesh_bind_group",
500            &pipeline_cache.get_bind_group_layout(&self.morphed_motion),
501            &[
502                entry::model(0, model.clone()),
503                entry::weights(2, current_weights),
504                entry::targets(3, targets),
505                entry::weights(7, prev_weights),
506            ],
507        )
508    }
509
510    /// Creates the bind group for meshes with skins and morph targets.
511    pub fn morphed_skinned(
512        &self,
513        render_device: &RenderDevice,
514        pipeline_cache: &PipelineCache,
515        model: &BindingResource,
516        current_skin: &Buffer,
517        current_weights: &Buffer,
518        targets: &TextureView,
519    ) -> BindGroup {
520        render_device.create_bind_group(
521            "morphed_skinned_mesh_bind_group",
522            &pipeline_cache.get_bind_group_layout(&self.morphed_skinned),
523            &[
524                entry::model(0, model.clone()),
525                entry::skinning(render_device, 1, current_skin),
526                entry::weights(2, current_weights),
527                entry::targets(3, targets),
528            ],
529        )
530    }
531
532    /// Creates the bind group for meshes with skins and morph targets, in
533    /// addition to the infrastructure to compute motion vectors.
534    ///
535    /// See the documentation for [`MeshLayouts::skinned_motion`] and
536    /// [`MeshLayouts::morphed_motion`] above for more information about the
537    /// `current_skin`, `prev_skin`, `current_weights`, and `prev_weights`
538    /// buffers.
539    pub fn morphed_skinned_motion(
540        &self,
541        render_device: &RenderDevice,
542        pipeline_cache: &PipelineCache,
543        model: &BindingResource,
544        current_skin: &Buffer,
545        current_weights: &Buffer,
546        targets: &TextureView,
547        prev_skin: &Buffer,
548        prev_weights: &Buffer,
549    ) -> BindGroup {
550        render_device.create_bind_group(
551            "morphed_skinned_motion_mesh_bind_group",
552            &pipeline_cache.get_bind_group_layout(&self.morphed_skinned_motion),
553            &[
554                entry::model(0, model.clone()),
555                entry::skinning(render_device, 1, current_skin),
556                entry::weights(2, current_weights),
557                entry::targets(3, targets),
558                entry::skinning(render_device, 6, prev_skin),
559                entry::weights(7, prev_weights),
560            ],
561        )
562    }
563}