Skip to content

Commit 2daa5c2

Browse files
committed
Add dynamic vertex buffer layouts to resolve sorting issues
- Vertex buffer layouts are set on `Mesh` for shader specialization. `Mesh` knows how to specialize its own vertex buffer because it owns the vertex data. - The `RenderAsset` implementation for `Mesh` caches the vertex buffer layout and adds a cache key to the `GpuMesh` render asset. The `MeshPipeline` can lookup the vertex buffer layout by composing `VertexLayoutKey` within `MeshPipelineKey`. - `SpritePipeline` and `UiPipeline` populates the vertex layout cache for themselves.. - `SpecializedPipeline::specialize` now takes a reference to the `RenderPipelineCache`, which is the best way I could find to get a reference to the cache for vertex layout lookups. - Discussion: https://discord.com/channels/691052431525675048/743663924229963868/908484759833960489
1 parent 7356f15 commit 2daa5c2

File tree

13 files changed

+427
-293
lines changed

13 files changed

+427
-293
lines changed

crates/bevy_gltf/src/loader.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use bevy_render::{
1414
color::Color,
1515
mesh::{Indices, Mesh, VertexAttributeValues},
1616
primitives::{Aabb, Frustum},
17+
render_resource::VertexFormat,
1718
texture::{Image, ImageType, TextureError},
1819
view::VisibleEntities,
1920
};
@@ -133,6 +134,8 @@ async fn load_gltf<'a, 'b>(
133134
.read_tangents()
134135
.map(|v| VertexAttributeValues::Float32x4(v.collect()))
135136
{
137+
mesh.vertex_layout_mut()
138+
.push(Mesh::ATTRIBUTE_TANGENT, VertexFormat::Float32x4);
136139
mesh.set_attribute(Mesh::ATTRIBUTE_TANGENT, vertex_attribute);
137140
}
138141

crates/bevy_pbr/src/render/light.rs

Lines changed: 9 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use bevy_math::{const_vec3, Mat4, UVec3, UVec4, Vec2, Vec3, Vec4, Vec4Swizzles};
1414
use bevy_render::{
1515
camera::{Camera, CameraProjection},
1616
color::Color,
17-
mesh::Mesh,
17+
mesh::{Mesh, VertexLayoutKey},
1818
render_asset::RenderAssets,
1919
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
2020
render_phase::{
@@ -207,84 +207,21 @@ impl FromWorld for ShadowPipeline {
207207
}
208208
}
209209

210-
bitflags::bitflags! {
211-
#[repr(transparent)]
212-
pub struct ShadowPipelineKey: u32 {
213-
const NONE = 0;
214-
const VERTEX_TANGENTS = (1 << 0);
215-
}
216-
}
210+
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
211+
pub struct ShadowPipelineKey(pub VertexLayoutKey);
217212

218213
impl SpecializedPipeline for ShadowPipeline {
219214
type Key = ShadowPipelineKey;
220215

221-
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
222-
let (vertex_array_stride, vertex_attributes) =
223-
if key.contains(ShadowPipelineKey::VERTEX_TANGENTS) {
224-
(
225-
48,
226-
vec![
227-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
228-
VertexAttribute {
229-
format: VertexFormat::Float32x3,
230-
offset: 12,
231-
shader_location: 0,
232-
},
233-
// Normal
234-
VertexAttribute {
235-
format: VertexFormat::Float32x3,
236-
offset: 0,
237-
shader_location: 1,
238-
},
239-
// Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically))
240-
VertexAttribute {
241-
format: VertexFormat::Float32x2,
242-
offset: 40,
243-
shader_location: 2,
244-
},
245-
// Tangent
246-
VertexAttribute {
247-
format: VertexFormat::Float32x4,
248-
offset: 24,
249-
shader_location: 3,
250-
},
251-
],
252-
)
253-
} else {
254-
(
255-
32,
256-
vec![
257-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
258-
VertexAttribute {
259-
format: VertexFormat::Float32x3,
260-
offset: 12,
261-
shader_location: 0,
262-
},
263-
// Normal
264-
VertexAttribute {
265-
format: VertexFormat::Float32x3,
266-
offset: 0,
267-
shader_location: 1,
268-
},
269-
// Uv
270-
VertexAttribute {
271-
format: VertexFormat::Float32x2,
272-
offset: 24,
273-
shader_location: 2,
274-
},
275-
],
276-
)
277-
};
216+
fn specialize(&self, cache: &RenderPipelineCache, key: Self::Key) -> RenderPipelineDescriptor {
217+
let vertex_layout = cache.vertex_layout_cache.get(&key.0).unwrap();
218+
278219
RenderPipelineDescriptor {
279220
vertex: VertexState {
280221
shader: SHADOW_SHADER_HANDLE.typed::<Shader>(),
281222
entry_point: "vertex".into(),
282223
shader_defs: vec![],
283-
buffers: vec![VertexBufferLayout {
284-
array_stride: vertex_array_stride,
285-
step_mode: VertexStepMode::Vertex,
286-
attributes: vertex_attributes,
287-
}],
224+
buffers: vec![vertex_layout.clone()],
288225
},
289226
fragment: None,
290227
layout: Some(vec![self.view_layout.clone(), self.mesh_layout.clone()]),
@@ -1063,13 +1000,9 @@ pub fn queue_shadows(
10631000
// NOTE: Lights with shadow mapping disabled will have no visible entities
10641001
// so no meshes will be queued
10651002
for entity in visible_entities.iter().copied() {
1066-
let mut key = ShadowPipelineKey::empty();
10671003
if let Ok(mesh_handle) = casting_meshes.get(entity) {
1068-
if let Some(mesh) = render_meshes.get(mesh_handle) {
1069-
if mesh.has_tangents {
1070-
key |= ShadowPipelineKey::VERTEX_TANGENTS;
1071-
}
1072-
}
1004+
let mesh = render_meshes.get(mesh_handle).unwrap();
1005+
let key = ShadowPipelineKey(mesh.vertex_layout_key);
10731006
let pipeline_id =
10741007
pipelines.specialize(&mut pipeline_cache, &shadow_pipeline, key);
10751008

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 23 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use bevy_ecs::{
1111
use bevy_math::Mat4;
1212
use bevy_reflect::TypeUuid;
1313
use bevy_render::{
14-
mesh::Mesh,
14+
mesh::{Mesh, VertexLayoutKey},
1515
render_asset::RenderAssets,
1616
render_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
1717
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
@@ -361,25 +361,31 @@ impl MeshPipeline {
361361
}
362362
}
363363

364+
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
365+
pub struct MeshPipelineKey {
366+
pub vertex_layout_key: VertexLayoutKey,
367+
pub flags: MeshPipelineFlags,
368+
}
369+
364370
bitflags::bitflags! {
365371
#[repr(transparent)]
366372
// NOTE: Apparently quadro drivers support up to 64x MSAA.
367373
/// MSAA uses the highest 6 bits for the MSAA sample count - 1 to support up to 64x MSAA.
368-
pub struct MeshPipelineKey: u32 {
374+
pub struct MeshPipelineFlags: u32 {
369375
const NONE = 0;
370376
const VERTEX_TANGENTS = (1 << 0);
371377
const TRANSPARENT_MAIN_PASS = (1 << 1);
372-
const MSAA_RESERVED_BITS = MeshPipelineKey::MSAA_MASK_BITS << MeshPipelineKey::MSAA_SHIFT_BITS;
378+
const MSAA_RESERVED_BITS = MeshPipelineFlags::MSAA_MASK_BITS << MeshPipelineFlags::MSAA_SHIFT_BITS;
373379
}
374380
}
375381

376-
impl MeshPipelineKey {
382+
impl MeshPipelineFlags {
377383
const MSAA_MASK_BITS: u32 = 0b111111;
378384
const MSAA_SHIFT_BITS: u32 = 32 - 6;
379385

380386
pub fn from_msaa_samples(msaa_samples: u32) -> Self {
381387
let msaa_bits = ((msaa_samples - 1) & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
382-
MeshPipelineKey::from_bits(msaa_bits).unwrap()
388+
MeshPipelineFlags::from_bits(msaa_bits).unwrap()
383389
}
384390

385391
pub fn msaa_samples(&self) -> u32 {
@@ -390,70 +396,19 @@ impl MeshPipelineKey {
390396
impl SpecializedPipeline for MeshPipeline {
391397
type Key = MeshPipelineKey;
392398

393-
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
394-
let (vertex_array_stride, vertex_attributes) =
395-
if key.contains(MeshPipelineKey::VERTEX_TANGENTS) {
396-
(
397-
48,
398-
vec![
399-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
400-
VertexAttribute {
401-
format: VertexFormat::Float32x3,
402-
offset: 12,
403-
shader_location: 0,
404-
},
405-
// Normal
406-
VertexAttribute {
407-
format: VertexFormat::Float32x3,
408-
offset: 0,
409-
shader_location: 1,
410-
},
411-
// Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically))
412-
VertexAttribute {
413-
format: VertexFormat::Float32x2,
414-
offset: 40,
415-
shader_location: 2,
416-
},
417-
// Tangent
418-
VertexAttribute {
419-
format: VertexFormat::Float32x4,
420-
offset: 24,
421-
shader_location: 3,
422-
},
423-
],
424-
)
425-
} else {
426-
(
427-
32,
428-
vec![
429-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
430-
VertexAttribute {
431-
format: VertexFormat::Float32x3,
432-
offset: 12,
433-
shader_location: 0,
434-
},
435-
// Normal
436-
VertexAttribute {
437-
format: VertexFormat::Float32x3,
438-
offset: 0,
439-
shader_location: 1,
440-
},
441-
// Uv
442-
VertexAttribute {
443-
format: VertexFormat::Float32x2,
444-
offset: 24,
445-
shader_location: 2,
446-
},
447-
],
448-
)
449-
};
399+
fn specialize(&self, cache: &RenderPipelineCache, key: Self::Key) -> RenderPipelineDescriptor {
400+
let vertex_layout = cache
401+
.vertex_layout_cache
402+
.get(&key.vertex_layout_key)
403+
.unwrap();
404+
450405
let mut shader_defs = Vec::new();
451-
if key.contains(MeshPipelineKey::VERTEX_TANGENTS) {
406+
if key.flags.contains(MeshPipelineFlags::VERTEX_TANGENTS) {
452407
shader_defs.push(String::from("VERTEX_TANGENTS"));
453408
}
454409

455410
let (label, blend, depth_write_enabled);
456-
if key.contains(MeshPipelineKey::TRANSPARENT_MAIN_PASS) {
411+
if key.flags.contains(MeshPipelineFlags::TRANSPARENT_MAIN_PASS) {
457412
label = "transparent_mesh_pipeline".into();
458413
blend = Some(BlendState::ALPHA_BLENDING);
459414
// For the transparent pass, fragments that are closer will be alpha blended
@@ -473,11 +428,7 @@ impl SpecializedPipeline for MeshPipeline {
473428
shader: MESH_SHADER_HANDLE.typed::<Shader>(),
474429
entry_point: "vertex".into(),
475430
shader_defs: shader_defs.clone(),
476-
buffers: vec![VertexBufferLayout {
477-
array_stride: vertex_array_stride,
478-
step_mode: VertexStepMode::Vertex,
479-
attributes: vertex_attributes,
480-
}],
431+
buffers: vec![vertex_layout.clone()],
481432
},
482433
fragment: Some(FragmentState {
483434
shader: MESH_SHADER_HANDLE.typed::<Shader>(),
@@ -516,7 +467,7 @@ impl SpecializedPipeline for MeshPipeline {
516467
},
517468
}),
518469
multisample: MultisampleState {
519-
count: key.msaa_samples(),
470+
count: key.flags.msaa_samples(),
520471
mask: !0,
521472
alpha_to_coverage_enabled: false,
522473
},
@@ -710,11 +661,11 @@ impl EntityRenderCommand for DrawMesh {
710661

711662
#[cfg(test)]
712663
mod tests {
713-
use super::MeshPipelineKey;
664+
use super::MeshPipelineFlags;
714665
#[test]
715666
fn mesh_key_msaa_samples() {
716667
for i in 1..=64 {
717-
assert_eq!(MeshPipelineKey::from_msaa_samples(i).msaa_samples(), i);
668+
assert_eq!(MeshPipelineFlags::from_msaa_samples(i).msaa_samples(), i);
718669
}
719670
}
720671
}

crates/bevy_pbr/src/render/mod.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ pub struct PbrPipelineKey {
191191
impl SpecializedPipeline for PbrPipeline {
192192
type Key = PbrPipelineKey;
193193

194-
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
195-
let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key);
194+
fn specialize(&self, cache: &RenderPipelineCache, key: Self::Key) -> RenderPipelineDescriptor {
195+
let mut descriptor = self.mesh_pipeline.specialize(cache, key.mesh_key);
196196
descriptor.fragment.as_mut().unwrap().shader = PBR_SHADER_HANDLE.typed::<Shader>();
197197
descriptor.layout = Some(vec![
198198
self.mesh_pipeline.view_layout.clone(),
@@ -254,25 +254,24 @@ pub fn queue_meshes(
254254

255255
let inverse_view_matrix = view.transform.compute_matrix().inverse();
256256
let inverse_view_row_2 = inverse_view_matrix.row(2);
257-
let mesh_key = MeshPipelineKey::from_msaa_samples(msaa.samples);
257+
let flags = MeshPipelineFlags::from_msaa_samples(msaa.samples);
258258

259259
for visible_entity in &visible_entities.entities {
260260
if let Ok((material_handle, mesh_handle, mesh_uniform)) =
261261
standard_material_meshes.get(*visible_entity)
262262
{
263263
if let Some(material) = render_materials.get(material_handle) {
264+
let mesh = render_meshes.get(mesh_handle).unwrap();
264265
let mut pbr_key = PbrPipelineKey {
265-
mesh_key,
266+
mesh_key: MeshPipelineKey {
267+
vertex_layout_key: mesh.vertex_layout_key,
268+
flags,
269+
},
266270
normal_map: material.has_normal_map,
267271
};
268-
if let Some(mesh) = render_meshes.get(mesh_handle) {
269-
if mesh.has_tangents {
270-
pbr_key.mesh_key |= MeshPipelineKey::VERTEX_TANGENTS;
271-
}
272-
}
273272

274273
if let AlphaMode::Blend = material.alpha_mode {
275-
pbr_key.mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS
274+
pbr_key.mesh_key.flags |= MeshPipelineFlags::TRANSPARENT_MAIN_PASS
276275
}
277276

278277
let pipeline_id =

0 commit comments

Comments
 (0)