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        renderer::RenderDevice,
40    };
41
42    pub(super) fn model(render_device: &RenderDevice) -> BindGroupLayoutEntryBuilder {
43        GpuArrayBuffer::<MeshUniform>::binding_layout(render_device)
44            .visibility(ShaderStages::VERTEX_FRAGMENT)
45    }
46    pub(super) fn skinning(render_device: &RenderDevice) -> 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(render_device) {
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) {
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: BindGroupLayout,
168
169    /// Includes the lightmap texture and uniform.
170    pub lightmapped: BindGroupLayout,
171
172    /// Also includes the uniform for skinning
173    pub skinned: BindGroupLayout,
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: BindGroupLayout,
178
179    /// Also includes the uniform and [`MorphAttributes`] for morph targets.
180    ///
181    /// [`MorphAttributes`]: bevy_mesh::morph::MorphAttributes
182    pub morphed: BindGroupLayout,
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: BindGroupLayout,
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: BindGroupLayout,
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: BindGroupLayout,
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) -> BindGroupLayout {
220        render_device.create_bind_group_layout(
221            "mesh_layout",
222            &BindGroupLayoutEntries::single(
223                ShaderStages::empty(),
224                layout_entry::model(render_device),
225            ),
226        )
227    }
228
229    /// Creates the layout for skinned meshes.
230    fn skinned_layout(render_device: &RenderDevice) -> BindGroupLayout {
231        render_device.create_bind_group_layout(
232            "skinned_mesh_layout",
233            &BindGroupLayoutEntries::with_indices(
234                ShaderStages::VERTEX,
235                (
236                    (0, layout_entry::model(render_device)),
237                    // The current frame's joint matrix buffer.
238                    (1, layout_entry::skinning(render_device)),
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) -> BindGroupLayout {
247        render_device.create_bind_group_layout(
248            "skinned_motion_mesh_layout",
249            &BindGroupLayoutEntries::with_indices(
250                ShaderStages::VERTEX,
251                (
252                    (0, layout_entry::model(render_device)),
253                    // The current frame's joint matrix buffer.
254                    (1, layout_entry::skinning(render_device)),
255                    // The previous frame's joint matrix buffer.
256                    (6, layout_entry::skinning(render_device)),
257                ),
258            ),
259        )
260    }
261
262    /// Creates the layout for meshes with morph targets.
263    fn morphed_layout(render_device: &RenderDevice) -> BindGroupLayout {
264        render_device.create_bind_group_layout(
265            "morphed_mesh_layout",
266            &BindGroupLayoutEntries::with_indices(
267                ShaderStages::VERTEX,
268                (
269                    (0, layout_entry::model(render_device)),
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) -> BindGroupLayout {
281        render_device.create_bind_group_layout(
282            "morphed_mesh_layout",
283            &BindGroupLayoutEntries::with_indices(
284                ShaderStages::VERTEX,
285                (
286                    (0, layout_entry::model(render_device)),
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) -> BindGroupLayout {
300        render_device.create_bind_group_layout(
301            "morphed_skinned_mesh_layout",
302            &BindGroupLayoutEntries::with_indices(
303                ShaderStages::VERTEX,
304                (
305                    (0, layout_entry::model(render_device)),
306                    // The current frame's joint matrix buffer.
307                    (1, layout_entry::skinning(render_device)),
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) -> BindGroupLayout {
319        render_device.create_bind_group_layout(
320            "morphed_skinned_motion_mesh_layout",
321            &BindGroupLayoutEntries::with_indices(
322                ShaderStages::VERTEX,
323                (
324                    (0, layout_entry::model(render_device)),
325                    // The current frame's joint matrix buffer.
326                    (1, layout_entry::skinning(render_device)),
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)),
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    ) -> BindGroupLayout {
343        if binding_arrays_are_usable(render_device, render_adapter) {
344            render_device.create_bind_group_layout(
345                "lightmapped_mesh_layout",
346                &BindGroupLayoutEntries::with_indices(
347                    ShaderStages::VERTEX,
348                    (
349                        (0, layout_entry::model(render_device)),
350                        (4, layout_entry::lightmaps_texture_view_array()),
351                        (5, layout_entry::lightmaps_sampler_array()),
352                    ),
353                ),
354            )
355        } else {
356            render_device.create_bind_group_layout(
357                "lightmapped_mesh_layout",
358                &BindGroupLayoutEntries::with_indices(
359                    ShaderStages::VERTEX,
360                    (
361                        (0, layout_entry::model(render_device)),
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(&self, render_device: &RenderDevice, model: &BindingResource) -> BindGroup {
373        render_device.create_bind_group(
374            "model_only_mesh_bind_group",
375            &self.model_only,
376            &[entry::model(0, model.clone())],
377        )
378    }
379
380    pub fn lightmapped(
381        &self,
382        render_device: &RenderDevice,
383        model: &BindingResource,
384        lightmap_slab: &LightmapSlab,
385        bindless_lightmaps: bool,
386    ) -> BindGroup {
387        if bindless_lightmaps {
388            let (texture_views, samplers) = lightmap_slab.build_binding_arrays();
389            render_device.create_bind_group(
390                "lightmapped_mesh_bind_group",
391                &self.lightmapped,
392                &[
393                    entry::model(0, model.clone()),
394                    entry::lightmaps_texture_view_array(4, &texture_views),
395                    entry::lightmaps_sampler_array(5, &samplers),
396                ],
397            )
398        } else {
399            let (texture_view, sampler) = lightmap_slab.bindings_for_first_lightmap();
400            render_device.create_bind_group(
401                "lightmapped_mesh_bind_group",
402                &self.lightmapped,
403                &[
404                    entry::model(0, model.clone()),
405                    entry::lightmaps_texture_view(4, texture_view),
406                    entry::lightmaps_sampler(5, sampler),
407                ],
408            )
409        }
410    }
411
412    /// Creates the bind group for skinned meshes with no morph targets.
413    pub fn skinned(
414        &self,
415        render_device: &RenderDevice,
416        model: &BindingResource,
417        current_skin: &Buffer,
418    ) -> BindGroup {
419        render_device.create_bind_group(
420            "skinned_mesh_bind_group",
421            &self.skinned,
422            &[
423                entry::model(0, model.clone()),
424                entry::skinning(render_device, 1, current_skin),
425            ],
426        )
427    }
428
429    /// Creates the bind group for skinned meshes with no morph targets, with
430    /// the infrastructure to compute motion vectors.
431    ///
432    /// `current_skin` is the buffer of joint matrices for this frame;
433    /// `prev_skin` is the buffer for the previous frame. The latter is used for
434    /// motion vector computation. If there is no such applicable buffer,
435    /// `current_skin` and `prev_skin` will reference the same buffer.
436    pub fn skinned_motion(
437        &self,
438        render_device: &RenderDevice,
439        model: &BindingResource,
440        current_skin: &Buffer,
441        prev_skin: &Buffer,
442    ) -> BindGroup {
443        render_device.create_bind_group(
444            "skinned_motion_mesh_bind_group",
445            &self.skinned_motion,
446            &[
447                entry::model(0, model.clone()),
448                entry::skinning(render_device, 1, current_skin),
449                entry::skinning(render_device, 6, prev_skin),
450            ],
451        )
452    }
453
454    /// Creates the bind group for meshes with no skins but morph targets.
455    pub fn morphed(
456        &self,
457        render_device: &RenderDevice,
458        model: &BindingResource,
459        current_weights: &Buffer,
460        targets: &TextureView,
461    ) -> BindGroup {
462        render_device.create_bind_group(
463            "morphed_mesh_bind_group",
464            &self.morphed,
465            &[
466                entry::model(0, model.clone()),
467                entry::weights(2, current_weights),
468                entry::targets(3, targets),
469            ],
470        )
471    }
472
473    /// Creates the bind group for meshes with no skins but morph targets, in
474    /// addition to the infrastructure to compute motion vectors.
475    ///
476    /// `current_weights` is the buffer of morph weights for this frame;
477    /// `prev_weights` is the buffer for the previous frame. The latter is used
478    /// for motion vector computation. If there is no such applicable buffer,
479    /// `current_weights` and `prev_weights` will reference the same buffer.
480    pub fn morphed_motion(
481        &self,
482        render_device: &RenderDevice,
483        model: &BindingResource,
484        current_weights: &Buffer,
485        targets: &TextureView,
486        prev_weights: &Buffer,
487    ) -> BindGroup {
488        render_device.create_bind_group(
489            "morphed_motion_mesh_bind_group",
490            &self.morphed_motion,
491            &[
492                entry::model(0, model.clone()),
493                entry::weights(2, current_weights),
494                entry::targets(3, targets),
495                entry::weights(7, prev_weights),
496            ],
497        )
498    }
499
500    /// Creates the bind group for meshes with skins and morph targets.
501    pub fn morphed_skinned(
502        &self,
503        render_device: &RenderDevice,
504        model: &BindingResource,
505        current_skin: &Buffer,
506        current_weights: &Buffer,
507        targets: &TextureView,
508    ) -> BindGroup {
509        render_device.create_bind_group(
510            "morphed_skinned_mesh_bind_group",
511            &self.morphed_skinned,
512            &[
513                entry::model(0, model.clone()),
514                entry::skinning(render_device, 1, current_skin),
515                entry::weights(2, current_weights),
516                entry::targets(3, targets),
517            ],
518        )
519    }
520
521    /// Creates the bind group for meshes with skins and morph targets, in
522    /// addition to the infrastructure to compute motion vectors.
523    ///
524    /// See the documentation for [`MeshLayouts::skinned_motion`] and
525    /// [`MeshLayouts::morphed_motion`] above for more information about the
526    /// `current_skin`, `prev_skin`, `current_weights`, and `prev_weights`
527    /// buffers.
528    pub fn morphed_skinned_motion(
529        &self,
530        render_device: &RenderDevice,
531        model: &BindingResource,
532        current_skin: &Buffer,
533        current_weights: &Buffer,
534        targets: &TextureView,
535        prev_skin: &Buffer,
536        prev_weights: &Buffer,
537    ) -> BindGroup {
538        render_device.create_bind_group(
539            "morphed_skinned_motion_mesh_bind_group",
540            &self.morphed_skinned_motion,
541            &[
542                entry::model(0, model.clone()),
543                entry::skinning(render_device, 1, current_skin),
544                entry::weights(2, current_weights),
545                entry::targets(3, targets),
546                entry::skinning(render_device, 6, prev_skin),
547                entry::weights(7, prev_weights),
548            ],
549        )
550    }
551}