From b7845f4b7d57fb5685aec14b80a1128bc114472a Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Fri, 11 Aug 2023 02:24:33 +0200 Subject: [PATCH 1/7] Reduce the size of MeshUniform Before: mat4x4 x3 u32 = 196 bytes, but in an array, practically 208 bytes for 16-byte alignment. After: mat3x4 x2 mat2x4 f32 u32 = 136 bytes, but in an array, practically 144 bytes for 16-byte alignment. That is a reduction of over 30% VRAM space and bandwidth usage, plus less data to serialize. --- assets/shaders/custom_vertex_attribute.wgsl | 4 +- assets/shaders/instancing.wgsl | 4 +- crates/bevy_pbr/src/light.rs | 6 +- crates/bevy_pbr/src/material.rs | 8 +- crates/bevy_pbr/src/prepass/mod.rs | 18 +-- crates/bevy_pbr/src/prepass/prepass.wgsl | 16 ++- crates/bevy_pbr/src/render/mesh.rs | 113 +++++++++++++++--- crates/bevy_pbr/src/render/mesh.wgsl | 16 ++- .../bevy_pbr/src/render/mesh_functions.wgsl | 27 ++++- crates/bevy_pbr/src/render/mesh_types.wgsl | 14 ++- crates/bevy_pbr/src/render/wireframe.wgsl | 8 +- crates/bevy_pbr/src/wireframe.rs | 12 +- crates/bevy_render/src/primitives/mod.rs | 33 ++--- .../src/render_phase/rangefinder.rs | 11 +- crates/bevy_render/src/view/visibility/mod.rs | 2 +- examples/shader/shader_instancing.rs | 8 +- 16 files changed, 211 insertions(+), 89 deletions(-) diff --git a/assets/shaders/custom_vertex_attribute.wgsl b/assets/shaders/custom_vertex_attribute.wgsl index 802c28df13190..a57c6df7a871f 100644 --- a/assets/shaders/custom_vertex_attribute.wgsl +++ b/assets/shaders/custom_vertex_attribute.wgsl @@ -1,5 +1,5 @@ #import bevy_pbr::mesh_bindings mesh -#import bevy_pbr::mesh_functions mesh_position_local_to_clip +#import bevy_pbr::mesh_functions affine_to_square, mesh_position_local_to_clip #import bevy_render::instance_index struct CustomMaterial { @@ -23,7 +23,7 @@ struct VertexOutput { fn vertex(vertex: Vertex) -> VertexOutput { var out: VertexOutput; out.clip_position = mesh_position_local_to_clip( - mesh[bevy_render::instance_index::get_instance_index(vertex.instance_index)].model, + affine_to_square(mesh[bevy_render::instance_index::get_instance_index(vertex.instance_index)].model), vec4(vertex.position, 1.0), ); out.blend_color = vertex.blend_color; diff --git a/assets/shaders/instancing.wgsl b/assets/shaders/instancing.wgsl index 055c9dbf4156e..dcfa81b98c70c 100644 --- a/assets/shaders/instancing.wgsl +++ b/assets/shaders/instancing.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_functions mesh_position_local_to_clip +#import bevy_pbr::mesh_functions affine_to_square, mesh_position_local_to_clip #import bevy_pbr::mesh_bindings mesh struct Vertex { @@ -24,7 +24,7 @@ fn vertex(vertex: Vertex) -> VertexOutput { // This index could be passed in via another uniform instead but it's // unnecessary for the example. out.clip_position = mesh_position_local_to_clip( - mesh[0].model, + affine_to_square(mesh[0].model), vec4(position, 1.0) ); out.color = vertex.i_color; diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 3e8c0d45177e6..08b21db7a3cd6 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -2025,7 +2025,7 @@ pub fn check_light_mesh_visibility( view_frusta.iter().zip(view_visible_entities) { // Disable near-plane culling, as a shadow caster could lie before the near plane. - if !frustum.intersects_obb(aabb, &transform.compute_matrix(), false, true) { + if !frustum.intersects_obb(aabb, &transform.affine(), false, true) { continue; } @@ -2098,7 +2098,7 @@ pub fn check_light_mesh_visibility( // If we have an aabb and transform, do frustum culling if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { - let model_to_world = transform.compute_matrix(); + let model_to_world = transform.affine(); // Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light if !light_sphere.intersects_obb(aabb, &model_to_world) { continue; @@ -2162,7 +2162,7 @@ pub fn check_light_mesh_visibility( // If we have an aabb and transform, do frustum culling if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { - let model_to_world = transform.compute_matrix(); + let model_to_world = transform.affine(); // Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light if !light_sphere.intersects_obb(aabb, &model_to_world) { continue; diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index e14c9f9e9ded9..00d7e3610348a 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -1,6 +1,6 @@ use crate::{ render, AlphaMode, DrawMesh, DrawPrepass, EnvironmentMapLight, MeshPipeline, MeshPipelineKey, - MeshUniform, PrepassPipelinePlugin, PrepassPlugin, RenderLightSystems, + MeshTransforms, MeshUniform, PrepassPipelinePlugin, PrepassPlugin, RenderLightSystems, ScreenSpaceAmbientOcclusionSettings, SetMeshBindGroup, SetMeshViewBindGroup, Shadow, }; use bevy_app::{App, Plugin}; @@ -382,7 +382,7 @@ pub fn queue_material_meshes( material_meshes: Query<( &Handle, &Handle, - &MeshUniform, + &MeshTransforms, &GpuArrayBufferIndex, )>, images: Res>, @@ -468,7 +468,7 @@ pub fn queue_material_meshes( let rangefinder = view.rangefinder3d(); for visible_entity in &visible_entities.entities { - if let Ok((material_handle, mesh_handle, mesh_uniform, batch_indices)) = + if let Ok((material_handle, mesh_handle, mesh_transforms, batch_indices)) = material_meshes.get(*visible_entity) { if let (Some(mesh), Some(material)) = ( @@ -516,7 +516,7 @@ pub fn queue_material_meshes( } }; - let distance = rangefinder.distance(&mesh_uniform.transform) + let distance = rangefinder.distance_affine(&mesh_transforms.transform) + material.properties.depth_bias; match material.properties.alpha_mode { AlphaMode::Opaque => { diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 9b1bde4b26d79..7f21ffba772cc 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -15,7 +15,7 @@ use bevy_ecs::{ SystemParamItem, }, }; -use bevy_math::Mat4; +use bevy_math::{Affine3A, Mat4}; use bevy_reflect::TypeUuid; use bevy_render::{ globals::{GlobalsBuffer, GlobalsUniform}, @@ -46,8 +46,8 @@ use bevy_utils::tracing::error; use crate::{ prepare_lights, setup_morph_and_skinning_defs, AlphaMode, DrawMesh, Material, MaterialPipeline, - MaterialPipelineKey, MeshLayouts, MeshPipeline, MeshPipelineKey, MeshUniform, RenderMaterials, - SetMaterialBindGroup, SetMeshBindGroup, + MaterialPipelineKey, MeshLayouts, MeshPipeline, MeshPipelineKey, MeshTransforms, MeshUniform, + RenderMaterials, SetMaterialBindGroup, SetMeshBindGroup, }; use std::{hash::Hash, marker::PhantomData}; @@ -203,7 +203,7 @@ pub fn update_previous_view_projections( } #[derive(Component)] -pub struct PreviousGlobalTransform(pub Mat4); +pub struct PreviousGlobalTransform(pub Affine3A); pub fn update_mesh_previous_global_transforms( mut commands: Commands, @@ -216,7 +216,7 @@ pub fn update_mesh_previous_global_transforms( for (entity, transform) in &meshes { commands .entity(entity) - .insert(PreviousGlobalTransform(transform.compute_matrix())); + .insert(PreviousGlobalTransform(transform.affine())); } } } @@ -762,7 +762,7 @@ pub fn queue_prepass_material_meshes( material_meshes: Query<( &Handle, &Handle, - &MeshUniform, + &MeshTransforms, &GpuArrayBufferIndex, )>, mut views: Query<( @@ -809,7 +809,7 @@ pub fn queue_prepass_material_meshes( let rangefinder = view.rangefinder3d(); for visible_entity in &visible_entities.entities { - let Ok((material_handle, mesh_handle, mesh_uniform, batch_indices)) = material_meshes.get(*visible_entity) else { + let Ok((material_handle, mesh_handle, mesh_transforms, batch_indices)) = material_meshes.get(*visible_entity) else { continue; }; @@ -852,8 +852,8 @@ pub fn queue_prepass_material_meshes( } }; - let distance = - rangefinder.distance(&mesh_uniform.transform) + material.properties.depth_bias; + let distance = rangefinder.distance_affine(&mesh_transforms.transform) + + material.properties.depth_bias; match alpha_mode { AlphaMode::Opaque => { opaque_phase.add(Opaque3dPrepass { diff --git a/crates/bevy_pbr/src/prepass/prepass.wgsl b/crates/bevy_pbr/src/prepass/prepass.wgsl index 82fa387f12687..8bf695ce98790 100644 --- a/crates/bevy_pbr/src/prepass/prepass.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass.wgsl @@ -3,7 +3,7 @@ #import bevy_pbr::skinning #import bevy_pbr::morph #import bevy_pbr::mesh_bindings mesh -#import bevy_render::instance_index +#import bevy_render::instance_index get_instance_index // Most of these attributes are not used in the default prepass fragment shader, but they are still needed so we can // pass them to custom prepass shaders like pbr_prepass.wgsl. @@ -92,7 +92,11 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { #else // SKINNED // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - var model = mesh[bevy_render::instance_index::get_instance_index(vertex_no_morph.instance_index)].model; + var model = bevy_pbr::mesh_functions::affine_to_square( + mesh[get_instance_index( + vertex_no_morph.instance_index + )].model + ); #endif // SKINNED out.clip_position = bevy_pbr::mesh_functions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); @@ -113,7 +117,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { vertex.normal, // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - bevy_render::instance_index::get_instance_index(vertex_no_morph.instance_index) + get_instance_index(vertex_no_morph.instance_index) ); #endif // SKINNED @@ -123,7 +127,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { vertex.tangent, // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - bevy_render::instance_index::get_instance_index(vertex_no_morph.instance_index) + get_instance_index(vertex_no_morph.instance_index) ); #endif // VERTEX_TANGENTS #endif // NORMAL_PREPASS @@ -133,7 +137,9 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 out.previous_world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world( - mesh[bevy_render::instance_index::get_instance_index(vertex_no_morph.instance_index)].previous_model, + bevy_pbr::mesh_functions::affine_to_square( + mesh[get_instance_index(vertex_no_morph.instance_index)].previous_model + ), vec4(vertex.position, 1.0) ); #endif // MOTION_VECTOR_PREPASS diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 46693e80978ac..7a129236dd443 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -18,11 +18,10 @@ use bevy_ecs::{ query::ROQueryItem, system::{lifetimeless::*, SystemParamItem, SystemState}, }; -use bevy_math::{Mat3A, Mat4, Vec2}; +use bevy_math::{Affine3A, Mat4, Vec2, Vec3Swizzles, Vec4}; use bevy_reflect::TypeUuid; use bevy_render::{ globals::{GlobalsBuffer, GlobalsUniform}, - gpu_component_array_buffer::GpuComponentArrayBufferPlugin, mesh::{ skinning::{SkinnedMesh, SkinnedMeshInverseBindposes}, GpuBufferInfo, InnerMeshVertexBufferLayout, Mesh, MeshVertexBufferLayout, @@ -115,8 +114,6 @@ impl Plugin for MeshRenderPlugin { load_internal_asset!(app, SKINNING_HANDLE, "skinning.wgsl", Shader::from_wgsl); load_internal_asset!(app, MORPH_HANDLE, "morph.wgsl", Shader::from_wgsl); - app.add_plugins(GpuComponentArrayBufferPlugin::::default()); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() @@ -129,6 +126,7 @@ impl Plugin for MeshRenderPlugin { .add_systems( Render, ( + prepare_mesh_uniforms.in_set(RenderSet::Prepare), prepare_skinned_meshes.in_set(RenderSet::Prepare), prepare_morphs.in_set(RenderSet::Prepare), queue_mesh_bind_group.in_set(RenderSet::Queue), @@ -151,7 +149,11 @@ impl Plugin for MeshRenderPlugin { )); } - render_app.init_resource::(); + render_app + .insert_resource(GpuArrayBuffer::::new( + render_app.world.resource::(), + )) + .init_resource::(); } // Load the mesh_bindings shader module here as it depends on runtime information about @@ -166,14 +168,73 @@ impl Plugin for MeshRenderPlugin { } } -#[derive(Component, ShaderType, Clone)] +#[derive(Component)] +pub struct MeshTransforms { + pub transform: Affine3A, + pub previous_transform: Affine3A, + pub flags: u32, +} + +#[derive(ShaderType, Clone)] pub struct MeshUniform { - pub transform: Mat4, - pub previous_transform: Mat4, - pub inverse_transpose_model: Mat4, + // Affine 4x3 matrices transposed to 3x4 + pub transform: [Vec4; 3], + pub previous_transform: [Vec4; 3], + // 3x3 matrix packed in mat2x4 and f32 as: + // [0].xyz, [1].x, + // [1].yz, [2].xy + // [2].z + pub inverse_transpose_model_a: [Vec4; 2], + pub inverse_transpose_model_b: f32, pub flags: u32, } +impl From<&MeshTransforms> for MeshUniform { + fn from(mesh_transforms: &MeshTransforms) -> Self { + let transpose_model_3x3 = mesh_transforms.transform.matrix3.transpose(); + let transpose_previous_model_3x3 = mesh_transforms.previous_transform.matrix3.transpose(); + let inverse_transpose_model_3x3 = mesh_transforms.transform.inverse().matrix3.transpose(); + Self { + transform: [ + transpose_model_3x3 + .x_axis + .extend(mesh_transforms.transform.translation.x), + transpose_model_3x3 + .y_axis + .extend(mesh_transforms.transform.translation.y), + transpose_model_3x3 + .z_axis + .extend(mesh_transforms.transform.translation.z), + ], + previous_transform: [ + transpose_previous_model_3x3 + .x_axis + .extend(mesh_transforms.previous_transform.translation.x), + transpose_previous_model_3x3 + .y_axis + .extend(mesh_transforms.previous_transform.translation.y), + transpose_previous_model_3x3 + .z_axis + .extend(mesh_transforms.previous_transform.translation.z), + ], + inverse_transpose_model_a: [ + ( + inverse_transpose_model_3x3.x_axis, + inverse_transpose_model_3x3.y_axis.x, + ) + .into(), + ( + inverse_transpose_model_3x3.y_axis.yz(), + inverse_transpose_model_3x3.z_axis.xy(), + ) + .into(), + ], + inverse_transpose_model_b: inverse_transpose_model_3x3.z_axis.z, + flags: mesh_transforms.flags, + } + } +} + // NOTE: These must match the bit flags in bevy_pbr/src/render/mesh_types.wgsl! bitflags::bitflags! { #[repr(transparent)] @@ -210,26 +271,25 @@ pub fn extract_meshes( for (entity, _, transform, previous_transform, handle, not_receiver, not_caster) in visible_meshes { - let transform = transform.compute_matrix(); + let transform = transform.affine(); let previous_transform = previous_transform.map(|t| t.0).unwrap_or(transform); let mut flags = if not_receiver.is_some() { MeshFlags::empty() } else { MeshFlags::SHADOW_RECEIVER }; - if Mat3A::from_mat4(transform).determinant().is_sign_positive() { + if transform.matrix3.determinant().is_sign_positive() { flags |= MeshFlags::SIGN_DETERMINANT_MODEL_3X3; } - let uniform = MeshUniform { + let transforms = MeshTransforms { flags: flags.bits(), transform, previous_transform, - inverse_transpose_model: transform.inverse().transpose(), }; if not_caster.is_some() { - not_caster_commands.push((entity, (handle.clone_weak(), uniform, NotShadowCaster))); + not_caster_commands.push((entity, (handle.clone_weak(), transforms, NotShadowCaster))); } else { - caster_commands.push((entity, (handle.clone_weak(), uniform))); + caster_commands.push((entity, (handle.clone_weak(), transforms))); } } *prev_caster_commands_len = caster_commands.len(); @@ -317,6 +377,29 @@ pub fn extract_skinned_meshes( commands.insert_or_spawn_batch(values); } +fn prepare_mesh_uniforms( + mut commands: Commands, + render_device: Res, + render_queue: Res, + mut gpu_array_buffer: ResMut>, + components: Query<(Entity, &MeshTransforms)>, +) { + gpu_array_buffer.clear(); + + let entities = components + .iter() + .map(|(entity, mesh_transforms)| { + ( + entity, + gpu_array_buffer.push(MeshUniform::from(mesh_transforms)), + ) + }) + .collect::>(); + commands.insert_or_spawn_batch(entities); + + gpu_array_buffer.write_buffer(&render_device, &render_queue); +} + #[derive(Resource, Clone)] pub struct MeshPipeline { pub view_layout: BindGroupLayout, diff --git a/crates/bevy_pbr/src/render/mesh.wgsl b/crates/bevy_pbr/src/render/mesh.wgsl index 106ec54ae041d..8174ba3d9e2c2 100644 --- a/crates/bevy_pbr/src/render/mesh.wgsl +++ b/crates/bevy_pbr/src/render/mesh.wgsl @@ -3,7 +3,7 @@ #import bevy_pbr::morph #import bevy_pbr::mesh_bindings mesh #import bevy_pbr::mesh_vertex_output MeshVertexOutput -#import bevy_render::instance_index +#import bevy_render::instance_index get_instance_index struct Vertex { @builtin(instance_index) instance_index: u32, @@ -66,8 +66,12 @@ fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput { var model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights); #else // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. - // See https://github.com/gfx-rs/naga/issues/2416 - var model = mesh[bevy_render::instance_index::get_instance_index(vertex_no_morph.instance_index)].model; + // See https://github.com/gfx-rs/naga/issues/2416 . + var model = mesh_functions::affine_to_square( + mesh[get_instance_index( + vertex_no_morph.instance_index + )].model + ); #endif #ifdef VERTEX_NORMALS @@ -78,7 +82,7 @@ fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput { vertex.normal, // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - bevy_render::instance_index::get_instance_index(vertex_no_morph.instance_index) + get_instance_index(vertex_no_morph.instance_index) ); #endif #endif @@ -98,7 +102,7 @@ fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput { vertex.tangent, // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - bevy_render::instance_index::get_instance_index(vertex_no_morph.instance_index) + get_instance_index(vertex_no_morph.instance_index) ); #endif @@ -109,7 +113,7 @@ fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput { #ifdef VERTEX_OUTPUT_INSTANCE_INDEX // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - out.instance_index = bevy_render::instance_index::get_instance_index(vertex_no_morph.instance_index); + out.instance_index = get_instance_index(vertex_no_morph.instance_index); #endif return out; diff --git a/crates/bevy_pbr/src/render/mesh_functions.wgsl b/crates/bevy_pbr/src/render/mesh_functions.wgsl index 4b5d1da7bdd26..793b2ba35f8e1 100644 --- a/crates/bevy_pbr/src/render/mesh_functions.wgsl +++ b/crates/bevy_pbr/src/render/mesh_functions.wgsl @@ -4,6 +4,26 @@ #import bevy_pbr::mesh_bindings mesh #import bevy_pbr::mesh_types MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT +fn affine_to_square(affine: mat3x4) -> mat4x4 { + return transpose(mat4x4( + affine[0], + affine[1], + affine[2], + vec4(0.0, 0.0, 0.0, 1.0), + )); +} + +fn mat2x4_f32_to_mat3x3( + a: mat2x4, + b: f32, +) -> mat3x3 { + return mat3x3( + a[0].xyz, + vec3(a[0].w, a[1].xy), + vec3(a[1].zw, b), + ); +} + fn mesh_position_local_to_world(model: mat4x4, vertex_position: vec4) -> vec4 { return model * vertex_position; } @@ -28,10 +48,9 @@ fn mesh_normal_local_to_world(vertex_normal: vec3, instance_index: u32) -> // unless you really know what you are doing. // http://www.mikktspace.com/ return normalize( - mat3x3( - mesh[instance_index].inverse_transpose_model[0].xyz, - mesh[instance_index].inverse_transpose_model[1].xyz, - mesh[instance_index].inverse_transpose_model[2].xyz + mat2x4_f32_to_mat3x3( + mesh[instance_index].inverse_transpose_model_a, + mesh[instance_index].inverse_transpose_model_b, ) * vertex_normal ); } diff --git a/crates/bevy_pbr/src/render/mesh_types.wgsl b/crates/bevy_pbr/src/render/mesh_types.wgsl index f8a775cc8fc73..9a0cf0fe25829 100644 --- a/crates/bevy_pbr/src/render/mesh_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_types.wgsl @@ -1,9 +1,17 @@ #define_import_path bevy_pbr::mesh_types struct Mesh { - model: mat4x4, - previous_model: mat4x4, - inverse_transpose_model: mat4x4, + // Affine 4x3 matrices transposed to 3x4 + // Use bevy_pbr::mesh_functions::affine_to_square to unpack + model: mat3x4, + previous_model: mat3x4, + // 3x3 matrix packed in mat2x4 and f32 as: + // [0].xyz, [1].x, + // [1].yz, [2].xy + // [2].z + // Use bevy_pbr::mesh_functions::mat2x4_f32_to_mat3x3 to unpack + inverse_transpose_model_a: mat2x4, + inverse_transpose_model_b: f32, // 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options. flags: u32, }; diff --git a/crates/bevy_pbr/src/render/wireframe.wgsl b/crates/bevy_pbr/src/render/wireframe.wgsl index 2b4f1126084a4..e02aa5af98323 100644 --- a/crates/bevy_pbr/src/render/wireframe.wgsl +++ b/crates/bevy_pbr/src/render/wireframe.wgsl @@ -1,5 +1,5 @@ #import bevy_pbr::mesh_bindings mesh -#import bevy_pbr::mesh_functions mesh_position_local_to_clip +#import bevy_pbr::mesh_functions affine_to_square, mesh_position_local_to_clip #import bevy_render::instance_index #ifdef SKINNED @@ -24,7 +24,11 @@ fn vertex(vertex: Vertex) -> VertexOutput { #ifdef SKINNED let model = bevy_pbr::skinning::skin_model(vertex.joint_indexes, vertex.joint_weights); #else - let model = mesh[bevy_render::instance_index::get_instance_index(vertex.instance_index)].model; + let model = affine_to_square( + mesh[bevy_render::instance_index::get_instance_index( + vertex.instance_index + )].model + ); #endif var out: VertexOutput; diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 1a6757abfe83e..75c243ea8d1c8 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -1,5 +1,5 @@ -use crate::MeshPipeline; use crate::{DrawMesh, MeshPipelineKey, MeshUniform, SetMeshBindGroup, SetMeshViewBindGroup}; +use crate::{MeshPipeline, MeshTransforms}; use bevy_app::Plugin; use bevy_asset::{load_internal_asset, Handle, HandleUntyped}; use bevy_core_pipeline::core_3d::Opaque3d; @@ -121,14 +121,14 @@ fn queue_wireframes( Query<( Entity, &Handle, - &MeshUniform, + &MeshTransforms, &GpuArrayBufferIndex, )>, Query< ( Entity, &Handle, - &MeshUniform, + &MeshTransforms, &GpuArrayBufferIndex, ), With, @@ -142,10 +142,10 @@ fn queue_wireframes( let rangefinder = view.rangefinder3d(); let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr); - let add_render_phase = |(entity, mesh_handle, mesh_uniform, batch_indices): ( + let add_render_phase = |(entity, mesh_handle, mesh_transforms, batch_indices): ( Entity, &Handle, - &MeshUniform, + &MeshTransforms, &GpuArrayBufferIndex, )| { if let Some(mesh) = render_meshes.get(mesh_handle) { @@ -164,7 +164,7 @@ fn queue_wireframes( entity, pipeline: pipeline_id, draw_function: draw_custom, - distance: rangefinder.distance(&mesh_uniform.transform), + distance: rangefinder.distance_affine(&mesh_transforms.transform), per_object_binding_dynamic_offset: batch_indices .dynamic_offset .unwrap_or_default(), diff --git a/crates/bevy_render/src/primitives/mod.rs b/crates/bevy_render/src/primitives/mod.rs index fc447a90cf8da..2cb067ded19f5 100644 --- a/crates/bevy_render/src/primitives/mod.rs +++ b/crates/bevy_render/src/primitives/mod.rs @@ -1,5 +1,5 @@ use bevy_ecs::{component::Component, prelude::Entity, reflect::ReflectComponent}; -use bevy_math::{Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles}; +use bevy_math::{Affine3A, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles}; use bevy_reflect::Reflect; use bevy_utils::HashMap; @@ -26,13 +26,13 @@ impl Aabb { /// Calculate the relative radius of the AABB with respect to a plane #[inline] - pub fn relative_radius(&self, p_normal: &Vec3A, axes: &[Vec3A]) -> f32 { + pub fn relative_radius(&self, p_normal: &Vec3A, model: &Mat3A) -> f32 { // NOTE: dot products on Vec3A use SIMD and even with the overhead of conversion are net faster than Vec3 let half_extents = self.half_extents; Vec3A::new( - p_normal.dot(axes[0]), - p_normal.dot(axes[1]), - p_normal.dot(axes[2]), + p_normal.dot(model.x_axis), + p_normal.dot(model.y_axis), + p_normal.dot(model.z_axis), ) .abs() .dot(half_extents) @@ -67,16 +67,11 @@ pub struct Sphere { impl Sphere { #[inline] - pub fn intersects_obb(&self, aabb: &Aabb, local_to_world: &Mat4) -> bool { - let aabb_center_world = *local_to_world * aabb.center.extend(1.0); - let axes = [ - Vec3A::from(local_to_world.x_axis), - Vec3A::from(local_to_world.y_axis), - Vec3A::from(local_to_world.z_axis), - ]; - let v = Vec3A::from(aabb_center_world) - self.center; + pub fn intersects_obb(&self, aabb: &Aabb, local_to_world: &Affine3A) -> bool { + let aabb_center_world = local_to_world.transform_point3a(aabb.center); + let v = aabb_center_world - self.center; let d = v.length(); - let relative_radius = aabb.relative_radius(&(v / d), &axes); + let relative_radius = aabb.relative_radius(&(v / d), &local_to_world.matrix3); d < self.radius + relative_radius } } @@ -195,17 +190,11 @@ impl Frustum { pub fn intersects_obb( &self, aabb: &Aabb, - model_to_world: &Mat4, + model_to_world: &Affine3A, intersect_near: bool, intersect_far: bool, ) -> bool { let aabb_center_world = model_to_world.transform_point3a(aabb.center).extend(1.0); - let axes = [ - Vec3A::from(model_to_world.x_axis), - Vec3A::from(model_to_world.y_axis), - Vec3A::from(model_to_world.z_axis), - ]; - for (idx, half_space) in self.half_spaces.into_iter().enumerate() { if idx == 4 && !intersect_near { continue; @@ -214,7 +203,7 @@ impl Frustum { continue; } let p_normal = half_space.normal(); - let relative_radius = aabb.relative_radius(&p_normal, &axes); + let relative_radius = aabb.relative_radius(&p_normal, &model_to_world.matrix3); if half_space.normal_d().dot(aabb_center_world) + relative_radius <= 0.0 { return false; } diff --git a/crates/bevy_render/src/render_phase/rangefinder.rs b/crates/bevy_render/src/render_phase/rangefinder.rs index 797782b9ccf66..ff6ae0421b87e 100644 --- a/crates/bevy_render/src/render_phase/rangefinder.rs +++ b/crates/bevy_render/src/render_phase/rangefinder.rs @@ -1,4 +1,4 @@ -use bevy_math::{Mat4, Vec4}; +use bevy_math::{Affine3A, Mat4, Vec4}; /// A distance calculator for the draw order of [`PhaseItem`](crate::render_phase::PhaseItem)s. pub struct ViewRangefinder3d { @@ -15,6 +15,15 @@ impl ViewRangefinder3d { } } + /// Calculates the distance, or view-space `Z` value, for the given `transform`. + #[inline] + pub fn distance_affine(&self, transform: &Affine3A) -> f32 { + // NOTE: row 2 of the inverse view matrix dotted with the translation from the model matrix + // gives the z component of translation of the mesh in view-space + self.inverse_view_row_2 + .dot(transform.translation.extend(1.0)) + } + /// Calculates the distance, or view-space `Z` value, for the given `transform`. #[inline] pub fn distance(&self, transform: &Mat4) -> f32 { diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 0e99f1cc09f78..9a58b4ada7aa2 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -392,7 +392,7 @@ pub fn check_visibility( // If we have an aabb and transform, do frustum culling if maybe_no_frustum_culling.is_none() { - let model = transform.compute_matrix(); + let model = transform.affine(); let model_sphere = Sphere { center: model.transform_point3a(model_aabb.center), radius: transform.radius_vec3a(model_aabb.half_extents), diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 389427f9eed8e..32d4e3df2e24d 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -6,7 +6,7 @@ use bevy::{ query::QueryItem, system::{lifetimeless::*, SystemParamItem}, }, - pbr::{MeshPipeline, MeshPipelineKey, MeshUniform, SetMeshBindGroup, SetMeshViewBindGroup}, + pbr::{MeshPipeline, MeshPipelineKey, MeshTransforms, SetMeshBindGroup, SetMeshViewBindGroup}, prelude::*, render::{ extract_component::{ExtractComponent, ExtractComponentPlugin}, @@ -113,7 +113,7 @@ fn queue_custom( mut pipelines: ResMut>, pipeline_cache: Res, meshes: Res>, - material_meshes: Query<(Entity, &MeshUniform, &Handle), With>, + material_meshes: Query<(Entity, &MeshTransforms, &Handle), With>, mut views: Query<(&ExtractedView, &mut RenderPhase)>, ) { let draw_custom = transparent_3d_draw_functions.read().id::(); @@ -123,7 +123,7 @@ fn queue_custom( for (view, mut transparent_phase) in &mut views { let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr); let rangefinder = view.rangefinder3d(); - for (entity, mesh_uniform, mesh_handle) in &material_meshes { + for (entity, mesh_transforms, mesh_handle) in &material_meshes { if let Some(mesh) = meshes.get(mesh_handle) { let key = view_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); @@ -134,7 +134,7 @@ fn queue_custom( entity, pipeline, draw_function: draw_custom, - distance: rangefinder.distance(&mesh_uniform.transform), + distance: rangefinder.distance_affine(&mesh_transforms.transform), }); } } From 100ac7642d04377179ecb9677d7e050f3131e5f1 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sat, 12 Aug 2023 13:09:46 +0200 Subject: [PATCH 2/7] bevy_math: Add Affine3 and conversions to/from Affine3A In rendering code there is a significant performance benefit from using only Mat3 + Vec3 instead of Mat3A + Vec3A for mesh transforms as their storage size dominates performance. --- crates/bevy_math/src/affine3.rs | 29 +++++++++++++++++++++++++++++ crates/bevy_math/src/lib.rs | 2 ++ 2 files changed, 31 insertions(+) create mode 100644 crates/bevy_math/src/affine3.rs diff --git a/crates/bevy_math/src/affine3.rs b/crates/bevy_math/src/affine3.rs new file mode 100644 index 0000000000000..734b7a071bf07 --- /dev/null +++ b/crates/bevy_math/src/affine3.rs @@ -0,0 +1,29 @@ +use glam::{Affine3A, Mat3, Vec3}; + +/// Reduced-size version of `glam::Affine3A` for use when storage has +/// significant performance impact. Convert to `glam::Affine3A` to do +/// non-trivial calculations. +pub struct Affine3 { + /// Scaling, rotation, shears, and other non-translation affine transforms + pub matrix3: Mat3, + /// Translation + pub translation: Vec3, +} + +impl From<&Affine3A> for Affine3 { + fn from(affine: &Affine3A) -> Self { + Self { + matrix3: affine.matrix3.into(), + translation: affine.translation.into(), + } + } +} + +impl Into for &Affine3 { + fn into(self) -> Affine3A { + Affine3A { + matrix3: self.matrix3.into(), + translation: self.translation.into(), + } + } +} diff --git a/crates/bevy_math/src/lib.rs b/crates/bevy_math/src/lib.rs index de0bc681c890c..1c75dc015d01b 100644 --- a/crates/bevy_math/src/lib.rs +++ b/crates/bevy_math/src/lib.rs @@ -7,10 +7,12 @@ #![allow(clippy::type_complexity)] #![warn(missing_docs)] +mod affine3; pub mod cubic_splines; mod ray; mod rects; +pub use affine3::*; pub use ray::Ray; pub use rects::*; From 71827d3323a5839212c6cadb91796ce1ee04a754 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sat, 12 Aug 2023 13:10:00 +0200 Subject: [PATCH 3/7] Use Affine3 for MeshTransforms --- crates/bevy_pbr/src/material.rs | 3 ++- crates/bevy_pbr/src/prepass/mod.rs | 2 +- crates/bevy_pbr/src/render/mesh.rs | 16 ++++++++++------ crates/bevy_pbr/src/wireframe.rs | 3 ++- .../bevy_render/src/render_phase/rangefinder.rs | 9 ++++----- examples/shader/shader_instancing.rs | 3 ++- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 00d7e3610348a..13e736d139e9a 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -516,7 +516,8 @@ pub fn queue_material_meshes( } }; - let distance = rangefinder.distance_affine(&mesh_transforms.transform) + let distance = rangefinder + .distance_translation(&mesh_transforms.transform.translation) + material.properties.depth_bias; match material.properties.alpha_mode { AlphaMode::Opaque => { diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 7f21ffba772cc..bfdd3ff0032cd 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -852,7 +852,7 @@ pub fn queue_prepass_material_meshes( } }; - let distance = rangefinder.distance_affine(&mesh_transforms.transform) + let distance = rangefinder.distance_translation(&mesh_transforms.transform.translation) + material.properties.depth_bias; match alpha_mode { AlphaMode::Opaque => { diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 7a129236dd443..3b03617b69bb5 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -18,7 +18,7 @@ use bevy_ecs::{ query::ROQueryItem, system::{lifetimeless::*, SystemParamItem, SystemState}, }; -use bevy_math::{Affine3A, Mat4, Vec2, Vec3Swizzles, Vec4}; +use bevy_math::{Affine3, Affine3A, Mat4, Vec2, Vec3Swizzles, Vec4}; use bevy_reflect::TypeUuid; use bevy_render::{ globals::{GlobalsBuffer, GlobalsUniform}, @@ -170,8 +170,8 @@ impl Plugin for MeshRenderPlugin { #[derive(Component)] pub struct MeshTransforms { - pub transform: Affine3A, - pub previous_transform: Affine3A, + pub transform: Affine3, + pub previous_transform: Affine3, pub flags: u32, } @@ -193,7 +193,11 @@ impl From<&MeshTransforms> for MeshUniform { fn from(mesh_transforms: &MeshTransforms) -> Self { let transpose_model_3x3 = mesh_transforms.transform.matrix3.transpose(); let transpose_previous_model_3x3 = mesh_transforms.previous_transform.matrix3.transpose(); - let inverse_transpose_model_3x3 = mesh_transforms.transform.inverse().matrix3.transpose(); + let inverse_transpose_model_3x3 = + <&Affine3 as Into>::into(&mesh_transforms.transform) + .inverse() + .matrix3 + .transpose(); Self { transform: [ transpose_model_3x3 @@ -282,9 +286,9 @@ pub fn extract_meshes( flags |= MeshFlags::SIGN_DETERMINANT_MODEL_3X3; } let transforms = MeshTransforms { + transform: (&transform).into(), + previous_transform: (&previous_transform).into(), flags: flags.bits(), - transform, - previous_transform, }; if not_caster.is_some() { not_caster_commands.push((entity, (handle.clone_weak(), transforms, NotShadowCaster))); diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 75c243ea8d1c8..cefb2c83d3f7e 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -164,7 +164,8 @@ fn queue_wireframes( entity, pipeline: pipeline_id, draw_function: draw_custom, - distance: rangefinder.distance_affine(&mesh_transforms.transform), + distance: rangefinder + .distance_translation(&mesh_transforms.transform.translation), per_object_binding_dynamic_offset: batch_indices .dynamic_offset .unwrap_or_default(), diff --git a/crates/bevy_render/src/render_phase/rangefinder.rs b/crates/bevy_render/src/render_phase/rangefinder.rs index ff6ae0421b87e..40e75183c8152 100644 --- a/crates/bevy_render/src/render_phase/rangefinder.rs +++ b/crates/bevy_render/src/render_phase/rangefinder.rs @@ -1,4 +1,4 @@ -use bevy_math::{Affine3A, Mat4, Vec4}; +use bevy_math::{Mat4, Vec3, Vec4}; /// A distance calculator for the draw order of [`PhaseItem`](crate::render_phase::PhaseItem)s. pub struct ViewRangefinder3d { @@ -15,13 +15,12 @@ impl ViewRangefinder3d { } } - /// Calculates the distance, or view-space `Z` value, for the given `transform`. + /// Calculates the distance, or view-space `Z` value, for the given `translation`. #[inline] - pub fn distance_affine(&self, transform: &Affine3A) -> f32 { + pub fn distance_translation(&self, translation: &Vec3) -> f32 { // NOTE: row 2 of the inverse view matrix dotted with the translation from the model matrix // gives the z component of translation of the mesh in view-space - self.inverse_view_row_2 - .dot(transform.translation.extend(1.0)) + self.inverse_view_row_2.dot(translation.extend(1.0)) } /// Calculates the distance, or view-space `Z` value, for the given `transform`. diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 32d4e3df2e24d..925c0c14d3404 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -134,7 +134,8 @@ fn queue_custom( entity, pipeline, draw_function: draw_custom, - distance: rangefinder.distance_affine(&mesh_transforms.transform), + distance: rangefinder + .distance_translation(&mesh_transforms.transform.translation), }); } } From 45b38d6edfd1d2ab45b36913f3a26c7327a503d4 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sun, 13 Aug 2023 13:17:39 +0200 Subject: [PATCH 4/7] Add underscore to mat2x4_f32_to_mat3x3 to make it usable outside the module This is a bug/limitation in naga_oil. --- crates/bevy_pbr/src/render/mesh_functions.wgsl | 4 ++-- crates/bevy_pbr/src/render/mesh_types.wgsl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/render/mesh_functions.wgsl b/crates/bevy_pbr/src/render/mesh_functions.wgsl index 793b2ba35f8e1..68f735dfa9fae 100644 --- a/crates/bevy_pbr/src/render/mesh_functions.wgsl +++ b/crates/bevy_pbr/src/render/mesh_functions.wgsl @@ -13,7 +13,7 @@ fn affine_to_square(affine: mat3x4) -> mat4x4 { )); } -fn mat2x4_f32_to_mat3x3( +fn mat2x4_f32_to_mat3x3_( a: mat2x4, b: f32, ) -> mat3x3 { @@ -48,7 +48,7 @@ fn mesh_normal_local_to_world(vertex_normal: vec3, instance_index: u32) -> // unless you really know what you are doing. // http://www.mikktspace.com/ return normalize( - mat2x4_f32_to_mat3x3( + mat2x4_f32_to_mat3x3_( mesh[instance_index].inverse_transpose_model_a, mesh[instance_index].inverse_transpose_model_b, ) * vertex_normal diff --git a/crates/bevy_pbr/src/render/mesh_types.wgsl b/crates/bevy_pbr/src/render/mesh_types.wgsl index 9a0cf0fe25829..f343006cabd5b 100644 --- a/crates/bevy_pbr/src/render/mesh_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_types.wgsl @@ -9,7 +9,7 @@ struct Mesh { // [0].xyz, [1].x, // [1].yz, [2].xy // [2].z - // Use bevy_pbr::mesh_functions::mat2x4_f32_to_mat3x3 to unpack + // Use bevy_pbr::mesh_functions::mat2x4_f32_to_mat3x3_ to unpack inverse_transpose_model_a: mat2x4, inverse_transpose_model_b: f32, // 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options. From dcb785c19250643cf1bc1e0dc73de93f25ff2719 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sun, 13 Aug 2023 13:25:01 +0200 Subject: [PATCH 5/7] Implement From<&Affine3> for Affine3A and use it --- crates/bevy_math/src/affine3.rs | 10 +++++----- crates/bevy_pbr/src/render/mesh.rs | 9 ++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/bevy_math/src/affine3.rs b/crates/bevy_math/src/affine3.rs index 734b7a071bf07..51598e4beae12 100644 --- a/crates/bevy_math/src/affine3.rs +++ b/crates/bevy_math/src/affine3.rs @@ -19,11 +19,11 @@ impl From<&Affine3A> for Affine3 { } } -impl Into for &Affine3 { - fn into(self) -> Affine3A { - Affine3A { - matrix3: self.matrix3.into(), - translation: self.translation.into(), +impl From<&Affine3> for Affine3A { + fn from(affine3: &Affine3) -> Self { + Self { + matrix3: affine3.matrix3.into(), + translation: affine3.translation.into(), } } } diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 3b03617b69bb5..6c61f05b8f4fc 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -193,11 +193,10 @@ impl From<&MeshTransforms> for MeshUniform { fn from(mesh_transforms: &MeshTransforms) -> Self { let transpose_model_3x3 = mesh_transforms.transform.matrix3.transpose(); let transpose_previous_model_3x3 = mesh_transforms.previous_transform.matrix3.transpose(); - let inverse_transpose_model_3x3 = - <&Affine3 as Into>::into(&mesh_transforms.transform) - .inverse() - .matrix3 - .transpose(); + let inverse_transpose_model_3x3 = Affine3A::from(&mesh_transforms.transform) + .inverse() + .matrix3 + .transpose(); Self { transform: [ transpose_model_3x3 From 34ef93c8d951bb3505c701ca347baf2b7636a957 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sun, 13 Aug 2023 13:38:39 +0200 Subject: [PATCH 6/7] Add get_(previous_)model_matrix shader helper functions This hides the ugliness and details that are unnecessary to know. --- assets/shaders/custom_vertex_attribute.wgsl | 5 ++--- assets/shaders/instancing.wgsl | 12 ++++++------ crates/bevy_pbr/src/prepass/prepass.wgsl | 10 ++-------- crates/bevy_pbr/src/render/mesh.wgsl | 6 +----- crates/bevy_pbr/src/render/mesh_functions.wgsl | 9 +++++++++ crates/bevy_pbr/src/render/wireframe.wgsl | 9 ++------- 6 files changed, 22 insertions(+), 29 deletions(-) diff --git a/assets/shaders/custom_vertex_attribute.wgsl b/assets/shaders/custom_vertex_attribute.wgsl index a57c6df7a871f..79454073a6fb4 100644 --- a/assets/shaders/custom_vertex_attribute.wgsl +++ b/assets/shaders/custom_vertex_attribute.wgsl @@ -1,6 +1,5 @@ #import bevy_pbr::mesh_bindings mesh -#import bevy_pbr::mesh_functions affine_to_square, mesh_position_local_to_clip -#import bevy_render::instance_index +#import bevy_pbr::mesh_functions get_model_matrix, mesh_position_local_to_clip struct CustomMaterial { color: vec4, @@ -23,7 +22,7 @@ struct VertexOutput { fn vertex(vertex: Vertex) -> VertexOutput { var out: VertexOutput; out.clip_position = mesh_position_local_to_clip( - affine_to_square(mesh[bevy_render::instance_index::get_instance_index(vertex.instance_index)].model), + get_model_matrix(vertex.instance_index), vec4(vertex.position, 1.0), ); out.blend_color = vertex.blend_color; diff --git a/assets/shaders/instancing.wgsl b/assets/shaders/instancing.wgsl index dcfa81b98c70c..a785b088be96f 100644 --- a/assets/shaders/instancing.wgsl +++ b/assets/shaders/instancing.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_functions affine_to_square, mesh_position_local_to_clip +#import bevy_pbr::mesh_functions get_model_matrix, mesh_position_local_to_clip #import bevy_pbr::mesh_bindings mesh struct Vertex { @@ -19,12 +19,12 @@ struct VertexOutput { fn vertex(vertex: Vertex) -> VertexOutput { let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz; var out: VertexOutput; - // NOTE: The 0 index into the Mesh array is a hack for this example as the - // instance_index builtin would map to the wrong index in the Mesh array. - // This index could be passed in via another uniform instead but it's - // unnecessary for the example. + // NOTE: Passing 0 as the instance_index to get_model_matrix() is a hack + // for this example as the instance_index builtin would map to the wrong + // index in the Mesh array. This index could be passed in via another + // uniform instead but it's unnecessary for the example. out.clip_position = mesh_position_local_to_clip( - affine_to_square(mesh[0].model), + get_model_matrix(0), vec4(position, 1.0) ); out.color = vertex.i_color; diff --git a/crates/bevy_pbr/src/prepass/prepass.wgsl b/crates/bevy_pbr/src/prepass/prepass.wgsl index 8bf695ce98790..396935ff411e7 100644 --- a/crates/bevy_pbr/src/prepass/prepass.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass.wgsl @@ -92,11 +92,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { #else // SKINNED // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 - var model = bevy_pbr::mesh_functions::affine_to_square( - mesh[get_instance_index( - vertex_no_morph.instance_index - )].model - ); + var model = bevy_pbr::mesh_functions::get_model_matrix(vertex_no_morph.instance_index); #endif // SKINNED out.clip_position = bevy_pbr::mesh_functions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); @@ -137,9 +133,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 out.previous_world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world( - bevy_pbr::mesh_functions::affine_to_square( - mesh[get_instance_index(vertex_no_morph.instance_index)].previous_model - ), + bevy_pbr::mesh_functions::get_previous_model_matrix(vertex_no_morph.instance_index), vec4(vertex.position, 1.0) ); #endif // MOTION_VECTOR_PREPASS diff --git a/crates/bevy_pbr/src/render/mesh.wgsl b/crates/bevy_pbr/src/render/mesh.wgsl index 8174ba3d9e2c2..0755c6859e478 100644 --- a/crates/bevy_pbr/src/render/mesh.wgsl +++ b/crates/bevy_pbr/src/render/mesh.wgsl @@ -67,11 +67,7 @@ fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput { #else // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // See https://github.com/gfx-rs/naga/issues/2416 . - var model = mesh_functions::affine_to_square( - mesh[get_instance_index( - vertex_no_morph.instance_index - )].model - ); + var model = mesh_functions::get_model_matrix(vertex_no_morph.instance_index); #endif #ifdef VERTEX_NORMALS diff --git a/crates/bevy_pbr/src/render/mesh_functions.wgsl b/crates/bevy_pbr/src/render/mesh_functions.wgsl index 68f735dfa9fae..738bea13f939a 100644 --- a/crates/bevy_pbr/src/render/mesh_functions.wgsl +++ b/crates/bevy_pbr/src/render/mesh_functions.wgsl @@ -3,6 +3,7 @@ #import bevy_pbr::mesh_view_bindings view #import bevy_pbr::mesh_bindings mesh #import bevy_pbr::mesh_types MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT +#import bevy_render::instance_index get_instance_index fn affine_to_square(affine: mat3x4) -> mat4x4 { return transpose(mat4x4( @@ -24,6 +25,14 @@ fn mat2x4_f32_to_mat3x3_( ); } +fn get_model_matrix(instance_index: u32) -> mat4x4 { + return affine_to_square(mesh[get_instance_index(instance_index)].model); +} + +fn get_previous_model_matrix(instance_index: u32) -> mat4x4 { + return affine_to_square(mesh[get_instance_index(instance_index)].previous_model); +} + fn mesh_position_local_to_world(model: mat4x4, vertex_position: vec4) -> vec4 { return model * vertex_position; } diff --git a/crates/bevy_pbr/src/render/wireframe.wgsl b/crates/bevy_pbr/src/render/wireframe.wgsl index e02aa5af98323..d44842b0b275c 100644 --- a/crates/bevy_pbr/src/render/wireframe.wgsl +++ b/crates/bevy_pbr/src/render/wireframe.wgsl @@ -1,6 +1,5 @@ #import bevy_pbr::mesh_bindings mesh -#import bevy_pbr::mesh_functions affine_to_square, mesh_position_local_to_clip -#import bevy_render::instance_index +#import bevy_pbr::mesh_functions get_model_matrix, mesh_position_local_to_clip #ifdef SKINNED #import bevy_pbr::skinning @@ -24,11 +23,7 @@ fn vertex(vertex: Vertex) -> VertexOutput { #ifdef SKINNED let model = bevy_pbr::skinning::skin_model(vertex.joint_indexes, vertex.joint_weights); #else - let model = affine_to_square( - mesh[bevy_render::instance_index::get_instance_index( - vertex.instance_index - )].model - ); + let model = get_model_matrix(vertex_no_morph.instance_index); #endif var out: VertexOutput; From e91458c5131f2abb8ec8cc548e57e308c1ab1796 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Mon, 14 Aug 2023 03:58:31 +0200 Subject: [PATCH 7/7] Rename mat2x4_f32_to_mat3x3_ to mat2x4_f32_to_mat3x3_unpack --- crates/bevy_pbr/src/render/mesh_functions.wgsl | 4 ++-- crates/bevy_pbr/src/render/mesh_types.wgsl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/render/mesh_functions.wgsl b/crates/bevy_pbr/src/render/mesh_functions.wgsl index 738bea13f939a..ff3a616335194 100644 --- a/crates/bevy_pbr/src/render/mesh_functions.wgsl +++ b/crates/bevy_pbr/src/render/mesh_functions.wgsl @@ -14,7 +14,7 @@ fn affine_to_square(affine: mat3x4) -> mat4x4 { )); } -fn mat2x4_f32_to_mat3x3_( +fn mat2x4_f32_to_mat3x3_unpack( a: mat2x4, b: f32, ) -> mat3x3 { @@ -57,7 +57,7 @@ fn mesh_normal_local_to_world(vertex_normal: vec3, instance_index: u32) -> // unless you really know what you are doing. // http://www.mikktspace.com/ return normalize( - mat2x4_f32_to_mat3x3_( + mat2x4_f32_to_mat3x3_unpack( mesh[instance_index].inverse_transpose_model_a, mesh[instance_index].inverse_transpose_model_b, ) * vertex_normal diff --git a/crates/bevy_pbr/src/render/mesh_types.wgsl b/crates/bevy_pbr/src/render/mesh_types.wgsl index f343006cabd5b..9b503364734b7 100644 --- a/crates/bevy_pbr/src/render/mesh_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_types.wgsl @@ -9,7 +9,7 @@ struct Mesh { // [0].xyz, [1].x, // [1].yz, [2].xy // [2].z - // Use bevy_pbr::mesh_functions::mat2x4_f32_to_mat3x3_ to unpack + // Use bevy_pbr::mesh_functions::mat2x4_f32_to_mat3x3_unpack to unpack inverse_transpose_model_a: mat2x4, inverse_transpose_model_b: f32, // 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options.