bevy/crates/bevy_pbr/src/render/mesh_bindings.rs
IceSentry 6d0c11a28f
Bind group layout entries (#10224)
# Objective

- Follow up to #9694

## Solution

- Same api as #9694 but adapted for `BindGroupLayoutEntry`
- Use the same `ShaderStages` visibilty for all entries by default
- Add `BindingType` helper function that mirror the wgsl equivalent and
that make writing layouts much simpler.

Before:
```rust
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
    label: Some("post_process_bind_group_layout"),
    entries: &[
        BindGroupLayoutEntry {
            binding: 0,
            visibility: ShaderStages::FRAGMENT,
            ty: BindingType::Texture {
                sample_type: TextureSampleType::Float { filterable: true },
                view_dimension: TextureViewDimension::D2,
                multisampled: false,
            },
            count: None,
        },
        BindGroupLayoutEntry {
            binding: 1,
            visibility: ShaderStages::FRAGMENT,
            ty: BindingType::Sampler(SamplerBindingType::Filtering),
            count: None,
        },
        BindGroupLayoutEntry {
            binding: 2,
            visibility: ShaderStages::FRAGMENT,
            ty: BindingType::Buffer {
                ty: bevy::render::render_resource::BufferBindingType::Uniform,
                has_dynamic_offset: false,
                min_binding_size: Some(PostProcessSettings::min_size()),
            },
            count: None,
        },
    ],
});
```
After:
```rust
let layout = render_device.create_bind_group_layout(
    "post_process_bind_group_layout"),
    &BindGroupLayoutEntries::sequential(
        ShaderStages::FRAGMENT,
        (
            texture_2d_f32(),
            sampler(SamplerBindingType::Filtering),
            uniform_buffer(false, Some(PostProcessSettings::min_size())),
        ),
    ),
);
```

Here's a more extreme example in bevy_solari:
86dab7f5da

---

## Changelog

- Added `BindGroupLayoutEntries` and all `BindingType` helper functions.

## Migration Guide

`RenderDevice::create_bind_group_layout()` doesn't take a
`BindGroupLayoutDescriptor` anymore. You need to provide the parameters
separately

```rust
// 0.12
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
    label: Some("post_process_bind_group_layout"),
    entries: &[
        BindGroupLayoutEntry {
			// ...
        },
    ],
});

// 0.13
let layout = render_device.create_bind_group_layout(
	"post_process_bind_group_layout",
    &[
        BindGroupLayoutEntry {
			// ...
        },
    ],
);
```

## TODO

- [x] implement a `Dynamic` variant
- [x] update the `RenderDevice::create_bind_group_layout()` api to match
the one from `RenderDevice::creat_bind_group()`
- [x] docs
2023-11-28 04:00:49 +00:00

219 lines
7.3 KiB
Rust

//! Bind group layout related definitions for the mesh pipeline.
use bevy_math::Mat4;
use bevy_render::{mesh::morph::MAX_MORPH_WEIGHTS, render_resource::*, renderer::RenderDevice};
use crate::render::skin::MAX_JOINTS;
const MORPH_WEIGHT_SIZE: usize = std::mem::size_of::<f32>();
pub const MORPH_BUFFER_SIZE: usize = MAX_MORPH_WEIGHTS * MORPH_WEIGHT_SIZE;
const JOINT_SIZE: usize = std::mem::size_of::<Mat4>();
pub(crate) const JOINT_BUFFER_SIZE: usize = MAX_JOINTS * JOINT_SIZE;
/// Individual layout entries.
mod layout_entry {
use super::{JOINT_BUFFER_SIZE, MORPH_BUFFER_SIZE};
use crate::MeshUniform;
use bevy_render::{
render_resource::{
binding_types::{texture_3d, uniform_buffer_sized},
BindGroupLayoutEntryBuilder, BufferSize, GpuArrayBuffer, ShaderStages,
TextureSampleType,
},
renderer::RenderDevice,
};
pub(super) fn model(render_device: &RenderDevice) -> BindGroupLayoutEntryBuilder {
GpuArrayBuffer::<MeshUniform>::binding_layout(render_device)
.visibility(ShaderStages::VERTEX_FRAGMENT)
}
pub(super) fn skinning() -> BindGroupLayoutEntryBuilder {
uniform_buffer_sized(true, BufferSize::new(JOINT_BUFFER_SIZE as u64))
}
pub(super) fn weights() -> BindGroupLayoutEntryBuilder {
uniform_buffer_sized(true, BufferSize::new(MORPH_BUFFER_SIZE as u64))
}
pub(super) fn targets() -> BindGroupLayoutEntryBuilder {
texture_3d(TextureSampleType::Float { filterable: false })
}
}
/// Individual [`BindGroupEntry`]
/// for bind groups.
mod entry {
use super::{JOINT_BUFFER_SIZE, MORPH_BUFFER_SIZE};
use bevy_render::render_resource::{
BindGroupEntry, BindingResource, Buffer, BufferBinding, BufferSize, TextureView,
};
fn entry(binding: u32, size: u64, buffer: &Buffer) -> BindGroupEntry {
BindGroupEntry {
binding,
resource: BindingResource::Buffer(BufferBinding {
buffer,
offset: 0,
size: Some(BufferSize::new(size).unwrap()),
}),
}
}
pub(super) fn model(binding: u32, resource: BindingResource) -> BindGroupEntry {
BindGroupEntry { binding, resource }
}
pub(super) fn skinning(binding: u32, buffer: &Buffer) -> BindGroupEntry {
entry(binding, JOINT_BUFFER_SIZE as u64, buffer)
}
pub(super) fn weights(binding: u32, buffer: &Buffer) -> BindGroupEntry {
entry(binding, MORPH_BUFFER_SIZE as u64, buffer)
}
pub(super) fn targets(binding: u32, texture: &TextureView) -> BindGroupEntry {
BindGroupEntry {
binding,
resource: BindingResource::TextureView(texture),
}
}
}
/// All possible [`BindGroupLayout`]s in bevy's default mesh shader (`mesh.wgsl`).
#[derive(Clone)]
pub struct MeshLayouts {
/// The mesh model uniform (transform) and nothing else.
pub model_only: BindGroupLayout,
/// Also includes the uniform for skinning
pub skinned: BindGroupLayout,
/// Also includes the uniform and [`MorphAttributes`] for morph targets.
///
/// [`MorphAttributes`]: bevy_render::mesh::morph::MorphAttributes
pub morphed: BindGroupLayout,
/// Also includes both uniforms for skinning and morph targets, also the
/// morph target [`MorphAttributes`] binding.
///
/// [`MorphAttributes`]: bevy_render::mesh::morph::MorphAttributes
pub morphed_skinned: BindGroupLayout,
}
impl MeshLayouts {
/// Prepare the layouts used by the default bevy [`Mesh`].
///
/// [`Mesh`]: bevy_render::prelude::Mesh
pub fn new(render_device: &RenderDevice) -> Self {
MeshLayouts {
model_only: Self::model_only_layout(render_device),
skinned: Self::skinned_layout(render_device),
morphed: Self::morphed_layout(render_device),
morphed_skinned: Self::morphed_skinned_layout(render_device),
}
}
// ---------- create individual BindGroupLayouts ----------
fn model_only_layout(render_device: &RenderDevice) -> BindGroupLayout {
render_device.create_bind_group_layout(
"mesh_layout",
&BindGroupLayoutEntries::single(
ShaderStages::empty(),
layout_entry::model(render_device),
),
)
}
fn skinned_layout(render_device: &RenderDevice) -> BindGroupLayout {
render_device.create_bind_group_layout(
"skinned_mesh_layout",
&BindGroupLayoutEntries::with_indices(
ShaderStages::VERTEX,
(
(0, layout_entry::model(render_device)),
(1, layout_entry::skinning()),
),
),
)
}
fn morphed_layout(render_device: &RenderDevice) -> BindGroupLayout {
render_device.create_bind_group_layout(
"morphed_mesh_layout",
&BindGroupLayoutEntries::with_indices(
ShaderStages::VERTEX,
(
(0, layout_entry::model(render_device)),
(2, layout_entry::weights()),
(3, layout_entry::targets()),
),
),
)
}
fn morphed_skinned_layout(render_device: &RenderDevice) -> BindGroupLayout {
render_device.create_bind_group_layout(
"morphed_skinned_mesh_layout",
&BindGroupLayoutEntries::with_indices(
ShaderStages::VERTEX,
(
(0, layout_entry::model(render_device)),
(1, layout_entry::skinning()),
(2, layout_entry::weights()),
(3, layout_entry::targets()),
),
),
)
}
// ---------- BindGroup methods ----------
pub fn model_only(&self, render_device: &RenderDevice, model: &BindingResource) -> BindGroup {
render_device.create_bind_group(
"model_only_mesh_bind_group",
&self.model_only,
&[entry::model(0, model.clone())],
)
}
pub fn skinned(
&self,
render_device: &RenderDevice,
model: &BindingResource,
skin: &Buffer,
) -> BindGroup {
render_device.create_bind_group(
"skinned_mesh_bind_group",
&self.skinned,
&[entry::model(0, model.clone()), entry::skinning(1, skin)],
)
}
pub fn morphed(
&self,
render_device: &RenderDevice,
model: &BindingResource,
weights: &Buffer,
targets: &TextureView,
) -> BindGroup {
render_device.create_bind_group(
"morphed_mesh_bind_group",
&self.morphed,
&[
entry::model(0, model.clone()),
entry::weights(2, weights),
entry::targets(3, targets),
],
)
}
pub fn morphed_skinned(
&self,
render_device: &RenderDevice,
model: &BindingResource,
skin: &Buffer,
weights: &Buffer,
targets: &TextureView,
) -> BindGroup {
render_device.create_bind_group(
"morphed_skinned_mesh_bind_group",
&self.morphed_skinned,
&[
entry::model(0, model.clone()),
entry::skinning(1, skin),
entry::weights(2, weights),
entry::targets(3, targets),
],
)
}
}