bevy_pbr/render/
mesh_bindings.rs

1//! Bind group layout related definitions for the mesh pipeline.
2
3use bevy_math::Mat4;
4use bevy_render::{
5    mesh::morph::MAX_MORPH_WEIGHTS,
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(binding: u32, texture: &TextureView) -> BindGroupEntry {
129        BindGroupEntry {
130            binding,
131            resource: BindingResource::TextureView(texture),
132        }
133    }
134    pub(super) fn lightmaps_sampler(binding: u32, sampler: &Sampler) -> BindGroupEntry {
135        BindGroupEntry {
136            binding,
137            resource: BindingResource::Sampler(sampler),
138        }
139    }
140    pub(super) fn lightmaps_texture_view_array<'a>(
141        binding: u32,
142        textures: &'a [&'a WgpuTextureView],
143    ) -> BindGroupEntry<'a> {
144        BindGroupEntry {
145            binding,
146            resource: BindingResource::TextureViewArray(textures),
147        }
148    }
149    pub(super) fn lightmaps_sampler_array<'a>(
150        binding: u32,
151        samplers: &'a [&'a WgpuSampler],
152    ) -> BindGroupEntry<'a> {
153        BindGroupEntry {
154            binding,
155            resource: BindingResource::SamplerArray(samplers),
156        }
157    }
158}
159
160/// All possible [`BindGroupLayout`]s in bevy's default mesh shader (`mesh.wgsl`).
161#[derive(Clone)]
162pub struct MeshLayouts {
163    /// The mesh model uniform (transform) and nothing else.
164    pub model_only: BindGroupLayout,
165
166    /// Includes the lightmap texture and uniform.
167    pub lightmapped: BindGroupLayout,
168
169    /// Also includes the uniform for skinning
170    pub skinned: BindGroupLayout,
171
172    /// Like [`MeshLayouts::skinned`], but includes slots for the previous
173    /// frame's joint matrices, so that we can compute motion vectors.
174    pub skinned_motion: BindGroupLayout,
175
176    /// Also includes the uniform and [`MorphAttributes`] for morph targets.
177    ///
178    /// [`MorphAttributes`]: bevy_render::mesh::morph::MorphAttributes
179    pub morphed: BindGroupLayout,
180
181    /// Like [`MeshLayouts::morphed`], but includes a slot for the previous
182    /// frame's morph weights, so that we can compute motion vectors.
183    pub morphed_motion: BindGroupLayout,
184
185    /// Also includes both uniforms for skinning and morph targets, also the
186    /// morph target [`MorphAttributes`] binding.
187    ///
188    /// [`MorphAttributes`]: bevy_render::mesh::morph::MorphAttributes
189    pub morphed_skinned: BindGroupLayout,
190
191    /// Like [`MeshLayouts::morphed_skinned`], but includes slots for the
192    /// previous frame's joint matrices and morph weights, so that we can
193    /// compute motion vectors.
194    pub morphed_skinned_motion: BindGroupLayout,
195}
196
197impl MeshLayouts {
198    /// Prepare the layouts used by the default bevy [`Mesh`].
199    ///
200    /// [`Mesh`]: bevy_render::prelude::Mesh
201    pub fn new(render_device: &RenderDevice, render_adapter: &RenderAdapter) -> Self {
202        MeshLayouts {
203            model_only: Self::model_only_layout(render_device),
204            lightmapped: Self::lightmapped_layout(render_device, render_adapter),
205            skinned: Self::skinned_layout(render_device),
206            skinned_motion: Self::skinned_motion_layout(render_device),
207            morphed: Self::morphed_layout(render_device),
208            morphed_motion: Self::morphed_motion_layout(render_device),
209            morphed_skinned: Self::morphed_skinned_layout(render_device),
210            morphed_skinned_motion: Self::morphed_skinned_motion_layout(render_device),
211        }
212    }
213
214    // ---------- create individual BindGroupLayouts ----------
215
216    fn model_only_layout(render_device: &RenderDevice) -> BindGroupLayout {
217        render_device.create_bind_group_layout(
218            "mesh_layout",
219            &BindGroupLayoutEntries::single(
220                ShaderStages::empty(),
221                layout_entry::model(render_device),
222            ),
223        )
224    }
225
226    /// Creates the layout for skinned meshes.
227    fn skinned_layout(render_device: &RenderDevice) -> BindGroupLayout {
228        render_device.create_bind_group_layout(
229            "skinned_mesh_layout",
230            &BindGroupLayoutEntries::with_indices(
231                ShaderStages::VERTEX,
232                (
233                    (0, layout_entry::model(render_device)),
234                    // The current frame's joint matrix buffer.
235                    (1, layout_entry::skinning(render_device)),
236                ),
237            ),
238        )
239    }
240
241    /// Creates the layout for skinned meshes with the infrastructure to compute
242    /// motion vectors.
243    fn skinned_motion_layout(render_device: &RenderDevice) -> BindGroupLayout {
244        render_device.create_bind_group_layout(
245            "skinned_motion_mesh_layout",
246            &BindGroupLayoutEntries::with_indices(
247                ShaderStages::VERTEX,
248                (
249                    (0, layout_entry::model(render_device)),
250                    // The current frame's joint matrix buffer.
251                    (1, layout_entry::skinning(render_device)),
252                    // The previous frame's joint matrix buffer.
253                    (6, layout_entry::skinning(render_device)),
254                ),
255            ),
256        )
257    }
258
259    /// Creates the layout for meshes with morph targets.
260    fn morphed_layout(render_device: &RenderDevice) -> BindGroupLayout {
261        render_device.create_bind_group_layout(
262            "morphed_mesh_layout",
263            &BindGroupLayoutEntries::with_indices(
264                ShaderStages::VERTEX,
265                (
266                    (0, layout_entry::model(render_device)),
267                    // The current frame's morph weight buffer.
268                    (2, layout_entry::weights()),
269                    (3, layout_entry::targets()),
270                ),
271            ),
272        )
273    }
274
275    /// Creates the layout for meshes with morph targets and the infrastructure
276    /// to compute motion vectors.
277    fn morphed_motion_layout(render_device: &RenderDevice) -> BindGroupLayout {
278        render_device.create_bind_group_layout(
279            "morphed_mesh_layout",
280            &BindGroupLayoutEntries::with_indices(
281                ShaderStages::VERTEX,
282                (
283                    (0, layout_entry::model(render_device)),
284                    // The current frame's morph weight buffer.
285                    (2, layout_entry::weights()),
286                    (3, layout_entry::targets()),
287                    // The previous frame's morph weight buffer.
288                    (7, layout_entry::weights()),
289                ),
290            ),
291        )
292    }
293
294    /// Creates the bind group layout for meshes with both skins and morph
295    /// targets.
296    fn morphed_skinned_layout(render_device: &RenderDevice) -> BindGroupLayout {
297        render_device.create_bind_group_layout(
298            "morphed_skinned_mesh_layout",
299            &BindGroupLayoutEntries::with_indices(
300                ShaderStages::VERTEX,
301                (
302                    (0, layout_entry::model(render_device)),
303                    // The current frame's joint matrix buffer.
304                    (1, layout_entry::skinning(render_device)),
305                    // The current frame's morph weight buffer.
306                    (2, layout_entry::weights()),
307                    (3, layout_entry::targets()),
308                ),
309            ),
310        )
311    }
312
313    /// Creates the bind group layout for meshes with both skins and morph
314    /// targets, in addition to the infrastructure to compute motion vectors.
315    fn morphed_skinned_motion_layout(render_device: &RenderDevice) -> BindGroupLayout {
316        render_device.create_bind_group_layout(
317            "morphed_skinned_motion_mesh_layout",
318            &BindGroupLayoutEntries::with_indices(
319                ShaderStages::VERTEX,
320                (
321                    (0, layout_entry::model(render_device)),
322                    // The current frame's joint matrix buffer.
323                    (1, layout_entry::skinning(render_device)),
324                    // The current frame's morph weight buffer.
325                    (2, layout_entry::weights()),
326                    (3, layout_entry::targets()),
327                    // The previous frame's joint matrix buffer.
328                    (6, layout_entry::skinning(render_device)),
329                    // The previous frame's morph weight buffer.
330                    (7, layout_entry::weights()),
331                ),
332            ),
333        )
334    }
335
336    fn lightmapped_layout(
337        render_device: &RenderDevice,
338        render_adapter: &RenderAdapter,
339    ) -> BindGroupLayout {
340        if binding_arrays_are_usable(render_device, render_adapter) {
341            render_device.create_bind_group_layout(
342                "lightmapped_mesh_layout",
343                &BindGroupLayoutEntries::with_indices(
344                    ShaderStages::VERTEX,
345                    (
346                        (0, layout_entry::model(render_device)),
347                        (4, layout_entry::lightmaps_texture_view_array()),
348                        (5, layout_entry::lightmaps_sampler_array()),
349                    ),
350                ),
351            )
352        } else {
353            render_device.create_bind_group_layout(
354                "lightmapped_mesh_layout",
355                &BindGroupLayoutEntries::with_indices(
356                    ShaderStages::VERTEX,
357                    (
358                        (0, layout_entry::model(render_device)),
359                        (4, layout_entry::lightmaps_texture_view()),
360                        (5, layout_entry::lightmaps_sampler()),
361                    ),
362                ),
363            )
364        }
365    }
366
367    // ---------- BindGroup methods ----------
368
369    pub fn model_only(&self, render_device: &RenderDevice, model: &BindingResource) -> BindGroup {
370        render_device.create_bind_group(
371            "model_only_mesh_bind_group",
372            &self.model_only,
373            &[entry::model(0, model.clone())],
374        )
375    }
376
377    pub fn lightmapped(
378        &self,
379        render_device: &RenderDevice,
380        model: &BindingResource,
381        lightmap_slab: &LightmapSlab,
382        bindless_lightmaps: bool,
383    ) -> BindGroup {
384        if bindless_lightmaps {
385            let (texture_views, samplers) = lightmap_slab.build_binding_arrays();
386            render_device.create_bind_group(
387                "lightmapped_mesh_bind_group",
388                &self.lightmapped,
389                &[
390                    entry::model(0, model.clone()),
391                    entry::lightmaps_texture_view_array(4, &texture_views),
392                    entry::lightmaps_sampler_array(5, &samplers),
393                ],
394            )
395        } else {
396            let (texture_view, sampler) = lightmap_slab.bindings_for_first_lightmap();
397            render_device.create_bind_group(
398                "lightmapped_mesh_bind_group",
399                &self.lightmapped,
400                &[
401                    entry::model(0, model.clone()),
402                    entry::lightmaps_texture_view(4, texture_view),
403                    entry::lightmaps_sampler(5, sampler),
404                ],
405            )
406        }
407    }
408
409    /// Creates the bind group for skinned meshes with no morph targets.
410    pub fn skinned(
411        &self,
412        render_device: &RenderDevice,
413        model: &BindingResource,
414        current_skin: &Buffer,
415    ) -> BindGroup {
416        render_device.create_bind_group(
417            "skinned_mesh_bind_group",
418            &self.skinned,
419            &[
420                entry::model(0, model.clone()),
421                entry::skinning(render_device, 1, current_skin),
422            ],
423        )
424    }
425
426    /// Creates the bind group for skinned meshes with no morph targets, with
427    /// the infrastructure to compute motion vectors.
428    ///
429    /// `current_skin` is the buffer of joint matrices for this frame;
430    /// `prev_skin` is the buffer for the previous frame. The latter is used for
431    /// motion vector computation. If there is no such applicable buffer,
432    /// `current_skin` and `prev_skin` will reference the same buffer.
433    pub fn skinned_motion(
434        &self,
435        render_device: &RenderDevice,
436        model: &BindingResource,
437        current_skin: &Buffer,
438        prev_skin: &Buffer,
439    ) -> BindGroup {
440        render_device.create_bind_group(
441            "skinned_motion_mesh_bind_group",
442            &self.skinned_motion,
443            &[
444                entry::model(0, model.clone()),
445                entry::skinning(render_device, 1, current_skin),
446                entry::skinning(render_device, 6, prev_skin),
447            ],
448        )
449    }
450
451    /// Creates the bind group for meshes with no skins but morph targets.
452    pub fn morphed(
453        &self,
454        render_device: &RenderDevice,
455        model: &BindingResource,
456        current_weights: &Buffer,
457        targets: &TextureView,
458    ) -> BindGroup {
459        render_device.create_bind_group(
460            "morphed_mesh_bind_group",
461            &self.morphed,
462            &[
463                entry::model(0, model.clone()),
464                entry::weights(2, current_weights),
465                entry::targets(3, targets),
466            ],
467        )
468    }
469
470    /// Creates the bind group for meshes with no skins but morph targets, in
471    /// addition to the infrastructure to compute motion vectors.
472    ///
473    /// `current_weights` is the buffer of morph weights for this frame;
474    /// `prev_weights` is the buffer for the previous frame. The latter is used
475    /// for motion vector computation. If there is no such applicable buffer,
476    /// `current_weights` and `prev_weights` will reference the same buffer.
477    pub fn morphed_motion(
478        &self,
479        render_device: &RenderDevice,
480        model: &BindingResource,
481        current_weights: &Buffer,
482        targets: &TextureView,
483        prev_weights: &Buffer,
484    ) -> BindGroup {
485        render_device.create_bind_group(
486            "morphed_motion_mesh_bind_group",
487            &self.morphed_motion,
488            &[
489                entry::model(0, model.clone()),
490                entry::weights(2, current_weights),
491                entry::targets(3, targets),
492                entry::weights(7, prev_weights),
493            ],
494        )
495    }
496
497    /// Creates the bind group for meshes with skins and morph targets.
498    pub fn morphed_skinned(
499        &self,
500        render_device: &RenderDevice,
501        model: &BindingResource,
502        current_skin: &Buffer,
503        current_weights: &Buffer,
504        targets: &TextureView,
505    ) -> BindGroup {
506        render_device.create_bind_group(
507            "morphed_skinned_mesh_bind_group",
508            &self.morphed_skinned,
509            &[
510                entry::model(0, model.clone()),
511                entry::skinning(render_device, 1, current_skin),
512                entry::weights(2, current_weights),
513                entry::targets(3, targets),
514            ],
515        )
516    }
517
518    /// Creates the bind group for meshes with skins and morph targets, in
519    /// addition to the infrastructure to compute motion vectors.
520    ///
521    /// See the documentation for [`MeshLayouts::skinned_motion`] and
522    /// [`MeshLayouts::morphed_motion`] above for more information about the
523    /// `current_skin`, `prev_skin`, `current_weights`, and `prev_weights`
524    /// buffers.
525    pub fn morphed_skinned_motion(
526        &self,
527        render_device: &RenderDevice,
528        model: &BindingResource,
529        current_skin: &Buffer,
530        current_weights: &Buffer,
531        targets: &TextureView,
532        prev_skin: &Buffer,
533        prev_weights: &Buffer,
534    ) -> BindGroup {
535        render_device.create_bind_group(
536            "morphed_skinned_motion_mesh_bind_group",
537            &self.morphed_skinned_motion,
538            &[
539                entry::model(0, model.clone()),
540                entry::skinning(render_device, 1, current_skin),
541                entry::weights(2, current_weights),
542                entry::targets(3, targets),
543                entry::skinning(render_device, 6, prev_skin),
544                entry::weights(7, prev_weights),
545            ],
546        )
547    }
548}