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