Skip to content

Commit a159f2d

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 1bae879 commit a159f2d

File tree

12 files changed

+409
-278
lines changed

12 files changed

+409
-278
lines changed

crates/bevy_gltf/src/loader.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use bevy_render::{
1818
mesh::{Indices, Mesh, VertexAttributeValues},
1919
primitives::{Aabb, Frustum},
2020
render_resource::{
21-
AddressMode, FilterMode, PrimitiveTopology, SamplerDescriptor, TextureFormat,
21+
AddressMode, FilterMode, PrimitiveTopology, SamplerDescriptor, TextureFormat, VertexFormat,
2222
},
2323
texture::{Image, ImageType, TextureError},
2424
view::VisibleEntities,
@@ -138,6 +138,8 @@ async fn load_gltf<'a, 'b>(
138138
.read_tangents()
139139
.map(|v| VertexAttributeValues::Float32x4(v.collect()))
140140
{
141+
mesh.vertex_layout_mut()
142+
.push(Mesh::ATTRIBUTE_TANGENT, VertexFormat::Float32x4);
141143
mesh.set_attribute(Mesh::ATTRIBUTE_TANGENT, vertex_attribute);
142144
}
143145

crates/bevy_pbr/src/material.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
2-
AlphaMode, DrawMesh, MeshPipeline, MeshPipelineKey, MeshUniform, SetMeshBindGroup,
3-
SetMeshViewBindGroup,
2+
AlphaMode, DrawMesh, MeshPipeline, MeshPipelineFlags, MeshPipelineKey, MeshUniform,
3+
SetMeshBindGroup, SetMeshViewBindGroup,
44
};
55
use bevy_app::{App, Plugin};
66
use bevy_asset::{AddAsset, Asset, AssetServer, Handle};
@@ -205,8 +205,8 @@ pub struct MaterialPipeline<M: SpecializedMaterial> {
205205
impl<M: SpecializedMaterial> SpecializedPipeline for MaterialPipeline<M> {
206206
type Key = (MeshPipelineKey, M::Key);
207207

208-
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
209-
let mut descriptor = self.mesh_pipeline.specialize(key.0);
208+
fn specialize(&self, cache: &RenderPipelineCache, key: Self::Key) -> RenderPipelineDescriptor {
209+
let mut descriptor = self.mesh_pipeline.specialize(cache, key.0);
210210
if let Some(vertex_shader) = &self.vertex_shader {
211211
descriptor.vertex.shader = vertex_shader.clone();
212212
}
@@ -307,27 +307,33 @@ pub fn queue_material_meshes<M: SpecializedMaterial>(
307307

308308
let inverse_view_matrix = view.transform.compute_matrix().inverse();
309309
let inverse_view_row_2 = inverse_view_matrix.row(2);
310-
let mesh_key = MeshPipelineKey::from_msaa_samples(msaa.samples);
310+
let mesh_flags = MeshPipelineFlags::from_msaa_samples(msaa.samples);
311311

312312
for visible_entity in &visible_entities.entities {
313313
if let Ok((material_handle, mesh_handle, mesh_uniform)) =
314314
material_meshes.get(*visible_entity)
315315
{
316-
if let Some(material) = render_materials.get(material_handle) {
317-
let mut mesh_key = mesh_key;
318-
if let Some(mesh) = render_meshes.get(mesh_handle) {
319-
if mesh.has_tangents {
320-
mesh_key |= MeshPipelineKey::VERTEX_TANGENTS;
321-
}
322-
mesh_key |=
323-
MeshPipelineKey::from_primitive_topology(mesh.primitive_topology);
316+
if let (Some(material), Some(mesh)) = (
317+
render_materials.get(material_handle),
318+
render_meshes.get(mesh_handle),
319+
) {
320+
let mut mesh_flags = mesh_flags;
321+
if mesh.has_tangents {
322+
mesh_flags |= MeshPipelineFlags::VERTEX_TANGENTS;
324323
}
324+
mesh_flags |=
325+
MeshPipelineFlags::from_primitive_topology(mesh.primitive_topology);
325326
let alpha_mode = M::alpha_mode(material);
326327
if let AlphaMode::Blend = alpha_mode {
327-
mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS
328+
mesh_flags |= MeshPipelineFlags::TRANSPARENT_MAIN_PASS;
328329
}
329330

331+
let mesh_key = MeshPipelineKey {
332+
vertex_layout_key: mesh.vertex_layout_key,
333+
flags: mesh_flags,
334+
};
330335
let specialized_key = M::key(material);
336+
331337
let pipeline_id = pipelines.specialize(
332338
&mut pipeline_cache,
333339
&material_pipeline,

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::{
@@ -209,84 +209,21 @@ impl FromWorld for ShadowPipeline {
209209
}
210210
}
211211

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

220215
impl SpecializedPipeline for ShadowPipeline {
221216
type Key = ShadowPipelineKey;
222217

223-
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
224-
let (vertex_array_stride, vertex_attributes) =
225-
if key.contains(ShadowPipelineKey::VERTEX_TANGENTS) {
226-
(
227-
48,
228-
vec![
229-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
230-
VertexAttribute {
231-
format: VertexFormat::Float32x3,
232-
offset: 12,
233-
shader_location: 0,
234-
},
235-
// Normal
236-
VertexAttribute {
237-
format: VertexFormat::Float32x3,
238-
offset: 0,
239-
shader_location: 1,
240-
},
241-
// Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically))
242-
VertexAttribute {
243-
format: VertexFormat::Float32x2,
244-
offset: 40,
245-
shader_location: 2,
246-
},
247-
// Tangent
248-
VertexAttribute {
249-
format: VertexFormat::Float32x4,
250-
offset: 24,
251-
shader_location: 3,
252-
},
253-
],
254-
)
255-
} else {
256-
(
257-
32,
258-
vec![
259-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
260-
VertexAttribute {
261-
format: VertexFormat::Float32x3,
262-
offset: 12,
263-
shader_location: 0,
264-
},
265-
// Normal
266-
VertexAttribute {
267-
format: VertexFormat::Float32x3,
268-
offset: 0,
269-
shader_location: 1,
270-
},
271-
// Uv
272-
VertexAttribute {
273-
format: VertexFormat::Float32x2,
274-
offset: 24,
275-
shader_location: 2,
276-
},
277-
],
278-
)
279-
};
218+
fn specialize(&self, cache: &RenderPipelineCache, key: Self::Key) -> RenderPipelineDescriptor {
219+
let vertex_layout = cache.vertex_layout_cache.get(&key.0).unwrap();
220+
280221
RenderPipelineDescriptor {
281222
vertex: VertexState {
282223
shader: SHADOW_SHADER_HANDLE.typed::<Shader>(),
283224
entry_point: "vertex".into(),
284225
shader_defs: vec![],
285-
buffers: vec![VertexBufferLayout {
286-
array_stride: vertex_array_stride,
287-
step_mode: VertexStepMode::Vertex,
288-
attributes: vertex_attributes,
289-
}],
226+
buffers: vec![vertex_layout.clone()],
290227
},
291228
fragment: None,
292229
layout: Some(vec![self.view_layout.clone(), self.mesh_layout.clone()]),
@@ -1087,13 +1024,9 @@ pub fn queue_shadows(
10871024
// NOTE: Lights with shadow mapping disabled will have no visible entities
10881025
// so no meshes will be queued
10891026
for entity in visible_entities.iter().copied() {
1090-
let mut key = ShadowPipelineKey::empty();
10911027
if let Ok(mesh_handle) = casting_meshes.get(entity) {
1092-
if let Some(mesh) = render_meshes.get(mesh_handle) {
1093-
if mesh.has_tangents {
1094-
key |= ShadowPipelineKey::VERTEX_TANGENTS;
1095-
}
1096-
}
1028+
let mesh = render_meshes.get(mesh_handle).unwrap();
1029+
let key = ShadowPipelineKey(mesh.vertex_layout_key);
10971030
let pipeline_id =
10981031
pipelines.specialize(&mut pipeline_cache, &shadow_pipeline, key);
10991032

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 26 additions & 75 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::{GpuBufferInfo, Mesh},
14+
mesh::{GpuBufferInfo, Mesh, VertexLayoutKey},
1515
render_asset::RenderAssets,
1616
render_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
1717
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
@@ -356,28 +356,34 @@ impl MeshPipeline {
356356
}
357357
}
358358

359+
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
360+
pub struct MeshPipelineKey {
361+
pub vertex_layout_key: VertexLayoutKey,
362+
pub flags: MeshPipelineFlags,
363+
}
364+
359365
bitflags::bitflags! {
360366
#[repr(transparent)]
361367
// NOTE: Apparently quadro drivers support up to 64x MSAA.
362368
/// MSAA uses the highest 6 bits for the MSAA sample count - 1 to support up to 64x MSAA.
363-
pub struct MeshPipelineKey: u32 {
369+
pub struct MeshPipelineFlags: u32 {
364370
const NONE = 0;
365371
const VERTEX_TANGENTS = (1 << 0);
366372
const TRANSPARENT_MAIN_PASS = (1 << 1);
367-
const MSAA_RESERVED_BITS = MeshPipelineKey::MSAA_MASK_BITS << MeshPipelineKey::MSAA_SHIFT_BITS;
368-
const PRIMITIVE_TOPOLOGY_RESERVED_BITS = MeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS << MeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
373+
const MSAA_RESERVED_BITS = MeshPipelineFlags::MSAA_MASK_BITS << MeshPipelineFlags::MSAA_SHIFT_BITS;
374+
const PRIMITIVE_TOPOLOGY_RESERVED_BITS = MeshPipelineFlags::PRIMITIVE_TOPOLOGY_MASK_BITS << MeshPipelineFlags::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
369375
}
370376
}
371377

372-
impl MeshPipelineKey {
378+
impl MeshPipelineFlags {
373379
const MSAA_MASK_BITS: u32 = 0b111111;
374380
const MSAA_SHIFT_BITS: u32 = 32 - 6;
375381
const PRIMITIVE_TOPOLOGY_MASK_BITS: u32 = 0b111;
376382
const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u32 = Self::MSAA_SHIFT_BITS - 3;
377383

378384
pub fn from_msaa_samples(msaa_samples: u32) -> Self {
379385
let msaa_bits = ((msaa_samples - 1) & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
380-
MeshPipelineKey::from_bits(msaa_bits).unwrap()
386+
MeshPipelineFlags::from_bits(msaa_bits).unwrap()
381387
}
382388

383389
pub fn msaa_samples(&self) -> u32 {
@@ -388,7 +394,7 @@ impl MeshPipelineKey {
388394
let primitive_topology_bits = ((primitive_topology as u32)
389395
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS)
390396
<< Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
391-
MeshPipelineKey::from_bits(primitive_topology_bits).unwrap()
397+
MeshPipelineFlags::from_bits(primitive_topology_bits).unwrap()
392398
}
393399

394400
pub fn primitive_topology(&self) -> PrimitiveTopology {
@@ -408,70 +414,19 @@ impl MeshPipelineKey {
408414
impl SpecializedPipeline for MeshPipeline {
409415
type Key = MeshPipelineKey;
410416

411-
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
412-
let (vertex_array_stride, vertex_attributes) =
413-
if key.contains(MeshPipelineKey::VERTEX_TANGENTS) {
414-
(
415-
48,
416-
vec![
417-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
418-
VertexAttribute {
419-
format: VertexFormat::Float32x3,
420-
offset: 12,
421-
shader_location: 0,
422-
},
423-
// Normal
424-
VertexAttribute {
425-
format: VertexFormat::Float32x3,
426-
offset: 0,
427-
shader_location: 1,
428-
},
429-
// Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically))
430-
VertexAttribute {
431-
format: VertexFormat::Float32x2,
432-
offset: 40,
433-
shader_location: 2,
434-
},
435-
// Tangent
436-
VertexAttribute {
437-
format: VertexFormat::Float32x4,
438-
offset: 24,
439-
shader_location: 3,
440-
},
441-
],
442-
)
443-
} else {
444-
(
445-
32,
446-
vec![
447-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
448-
VertexAttribute {
449-
format: VertexFormat::Float32x3,
450-
offset: 12,
451-
shader_location: 0,
452-
},
453-
// Normal
454-
VertexAttribute {
455-
format: VertexFormat::Float32x3,
456-
offset: 0,
457-
shader_location: 1,
458-
},
459-
// Uv
460-
VertexAttribute {
461-
format: VertexFormat::Float32x2,
462-
offset: 24,
463-
shader_location: 2,
464-
},
465-
],
466-
)
467-
};
417+
fn specialize(&self, cache: &RenderPipelineCache, key: Self::Key) -> RenderPipelineDescriptor {
418+
let vertex_layout = cache
419+
.vertex_layout_cache
420+
.get(&key.vertex_layout_key)
421+
.unwrap();
422+
468423
let mut shader_defs = Vec::new();
469-
if key.contains(MeshPipelineKey::VERTEX_TANGENTS) {
424+
if key.flags.contains(MeshPipelineFlags::VERTEX_TANGENTS) {
470425
shader_defs.push(String::from("VERTEX_TANGENTS"));
471426
}
472427

473428
let (label, blend, depth_write_enabled);
474-
if key.contains(MeshPipelineKey::TRANSPARENT_MAIN_PASS) {
429+
if key.flags.contains(MeshPipelineFlags::TRANSPARENT_MAIN_PASS) {
475430
label = "transparent_mesh_pipeline".into();
476431
blend = Some(BlendState::ALPHA_BLENDING);
477432
// For the transparent pass, fragments that are closer will be alpha blended
@@ -494,11 +449,7 @@ impl SpecializedPipeline for MeshPipeline {
494449
shader: MESH_SHADER_HANDLE.typed::<Shader>(),
495450
entry_point: "vertex".into(),
496451
shader_defs: shader_defs.clone(),
497-
buffers: vec![VertexBufferLayout {
498-
array_stride: vertex_array_stride,
499-
step_mode: VertexStepMode::Vertex,
500-
attributes: vertex_attributes,
501-
}],
452+
buffers: vec![vertex_layout.clone()],
502453
},
503454
fragment: Some(FragmentState {
504455
shader: MESH_SHADER_HANDLE.typed::<Shader>(),
@@ -517,7 +468,7 @@ impl SpecializedPipeline for MeshPipeline {
517468
unclipped_depth: false,
518469
polygon_mode: PolygonMode::Fill,
519470
conservative: false,
520-
topology: key.primitive_topology(),
471+
topology: key.flags.primitive_topology(),
521472
strip_index_format: None,
522473
},
523474
depth_stencil: Some(DepthStencilState {
@@ -537,7 +488,7 @@ impl SpecializedPipeline for MeshPipeline {
537488
},
538489
}),
539490
multisample: MultisampleState {
540-
count: key.msaa_samples(),
491+
count: key.flags.msaa_samples(),
541492
mask: !0,
542493
alpha_to_coverage_enabled: false,
543494
},
@@ -737,11 +688,11 @@ impl EntityRenderCommand for DrawMesh {
737688

738689
#[cfg(test)]
739690
mod tests {
740-
use super::MeshPipelineKey;
691+
use super::MeshPipelineFlags;
741692
#[test]
742693
fn mesh_key_msaa_samples() {
743694
for i in 1..=64 {
744-
assert_eq!(MeshPipelineKey::from_msaa_samples(i).msaa_samples(), i);
695+
assert_eq!(MeshPipelineFlags::from_msaa_samples(i).msaa_samples(), i);
745696
}
746697
}
747698
}

0 commit comments

Comments
 (0)