Skip to content

Commit 226a778

Browse files
committed
Add dynamic vertex buffer layouts to resolve sorting issues
- Vertex buffer layoutss 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` copies the vertex buffer layout to the `GpuMesh` render asset. The pbr2 renderer uses it for pipeline specialization. - The sprite2 renderer uses hardcoded vertex buffer layouts. Passing it from `Sprite` down to shader specialization is more work: - `SpriteMeta` currently owns the vertex buffers. - Requires `extract_sprite()` to copy the descriptor to its output (`ExtractedSprites`) and then `prepare_sprite` would copy it to its output (`SpriteMeta`) and finally `queue_sprite` could create the specialized shader. - I experimented with using the pipeline cache, and it felt awfully forced. The shader pipelines and caches know nothing about the resources which own the vertex buffer data, and teaching them how to get along was causing many problems with dependency management, introducing new race conditions, and just plain bad code. - `SpecializedPipeline::specialize` accepts an argument for the vertex attribute buffer descriptor. - Discussion: https://discord.com/channels/691052431525675048/743663924229963868/908484759833960489
1 parent 1076a8f commit 226a778

File tree

10 files changed

+268
-212
lines changed

10 files changed

+268
-212
lines changed

examples/shader/custom_shader_pipelined.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,12 @@ pub struct CustomPipeline {
135135
impl SpecializedPipeline for CustomPipeline {
136136
type Key = PbrPipelineKey;
137137

138-
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
139-
let mut descriptor = self.pbr_pipeline.specialize(key);
138+
fn specialize(
139+
&self,
140+
key: Self::Key,
141+
vertex_layout: &VertexBufferLayout,
142+
) -> RenderPipelineDescriptor {
143+
let mut descriptor = self.pbr_pipeline.specialize(key, vertex_layout);
140144
descriptor.vertex.shader = self.shader.clone();
141145
descriptor.fragment.as_mut().unwrap().shader = self.shader.clone();
142146
descriptor.layout = Some(vec![
@@ -178,11 +182,12 @@ impl FromWorld for CustomPipeline {
178182
pub fn queue_custom(
179183
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
180184
materials: Res<RenderAssets<CustomMaterial>>,
185+
render_meshes: Res<RenderAssets<Mesh>>,
181186
custom_pipeline: Res<CustomPipeline>,
182187
mut pipeline_cache: ResMut<RenderPipelineCache>,
183188
mut specialized_pipelines: ResMut<SpecializedPipelines<CustomPipeline>>,
184189
msaa: Res<Msaa>,
185-
material_meshes: Query<(Entity, &Handle<CustomMaterial>, &MeshUniform), With<Handle<Mesh>>>,
190+
material_meshes: Query<(Entity, &Handle<CustomMaterial>, &Handle<Mesh>, &MeshUniform)>,
186191
mut views: Query<(&ExtractedView, &mut RenderPhase<Transparent3d>)>,
187192
) {
188193
let draw_custom = transparent_3d_draw_functions
@@ -193,14 +198,16 @@ pub fn queue_custom(
193198
for (view, mut transparent_phase) in views.iter_mut() {
194199
let view_matrix = view.transform.compute_matrix();
195200
let view_row_2 = view_matrix.row(2);
196-
for (entity, material_handle, mesh_uniform) in material_meshes.iter() {
201+
for (entity, material_handle, mesh_handle, mesh_uniform) in material_meshes.iter() {
197202
if materials.contains_key(material_handle) {
203+
let mesh = render_meshes.get(mesh_handle).unwrap();
198204
transparent_phase.add(Transparent3d {
199205
entity,
200206
pipeline: specialized_pipelines.specialize(
201207
&mut pipeline_cache,
202208
&custom_pipeline,
203209
key,
210+
&mesh.vertex_layout,
204211
),
205212
draw_function: draw_custom,
206213
distance: view_row_2.dot(mesh_uniform.transform.col(3)),

examples/shader/shader_defs_pipelined.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use bevy::{
1111
render2::{
1212
camera::PerspectiveCameraBundle,
1313
mesh::{shape, Mesh},
14+
render_asset::RenderAssets,
1415
render_component::{ExtractComponent, ExtractComponentPlugin},
1516
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
1617
render_resource::*,
@@ -105,12 +106,18 @@ impl FromWorld for IsRedPipeline {
105106
impl SpecializedPipeline for IsRedPipeline {
106107
type Key = (IsRed, PbrPipelineKey);
107108

108-
fn specialize(&self, (is_red, pbr_pipeline_key): Self::Key) -> RenderPipelineDescriptor {
109+
fn specialize(
110+
&self,
111+
(is_red, pbr_pipeline_key): Self::Key,
112+
vertex_layout: &VertexBufferLayout,
113+
) -> RenderPipelineDescriptor {
109114
let mut shader_defs = Vec::new();
110115
if is_red.0 {
111116
shader_defs.push("IS_RED".to_string());
112117
}
113-
let mut descriptor = self.pbr_pipeline.specialize(pbr_pipeline_key);
118+
let mut descriptor = self
119+
.pbr_pipeline
120+
.specialize(pbr_pipeline_key, vertex_layout);
114121
descriptor.vertex.shader = self.shader.clone();
115122
descriptor.vertex.shader_defs = shader_defs.clone();
116123
let fragment = descriptor.fragment.as_mut().unwrap();
@@ -131,13 +138,15 @@ type DrawIsRed = (
131138
DrawMesh,
132139
);
133140

141+
#[allow(clippy::too_many_arguments)]
134142
fn queue_custom(
135143
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
136144
custom_pipeline: Res<IsRedPipeline>,
137145
msaa: Res<Msaa>,
146+
render_meshes: Res<RenderAssets<Mesh>>,
138147
mut pipelines: ResMut<SpecializedPipelines<IsRedPipeline>>,
139148
mut pipeline_cache: ResMut<RenderPipelineCache>,
140-
material_meshes: Query<(Entity, &MeshUniform, &IsRed), With<Handle<Mesh>>>,
149+
material_meshes: Query<(Entity, &Handle<Mesh>, &MeshUniform, &IsRed)>,
141150
mut views: Query<(&ExtractedView, &mut RenderPhase<Transparent3d>)>,
142151
) {
143152
let draw_custom = transparent_3d_draw_functions
@@ -148,9 +157,14 @@ fn queue_custom(
148157
for (view, mut transparent_phase) in views.iter_mut() {
149158
let view_matrix = view.transform.compute_matrix();
150159
let view_row_2 = view_matrix.row(2);
151-
for (entity, mesh_uniform, is_red) in material_meshes.iter() {
152-
let pipeline =
153-
pipelines.specialize(&mut pipeline_cache, &custom_pipeline, (*is_red, key));
160+
for (entity, mesh_handle, mesh_uniform, is_red) in material_meshes.iter() {
161+
let mesh = render_meshes.get(mesh_handle).unwrap();
162+
let pipeline = pipelines.specialize(
163+
&mut pipeline_cache,
164+
&custom_pipeline,
165+
(*is_red, key),
166+
&mesh.vertex_layout,
167+
);
154168
transparent_phase.add(Transparent3d {
155169
entity,
156170
pipeline,

pipelined/bevy_gltf2/src/loader.rs

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

pipelined/bevy_pbr2/src/render/light.rs

Lines changed: 15 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -180,73 +180,17 @@ bitflags::bitflags! {
180180
impl SpecializedPipeline for ShadowPipeline {
181181
type Key = ShadowPipelineKey;
182182

183-
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
184-
let (vertex_array_stride, vertex_attributes) =
185-
if key.contains(ShadowPipelineKey::VERTEX_TANGENTS) {
186-
(
187-
48,
188-
vec![
189-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
190-
VertexAttribute {
191-
format: VertexFormat::Float32x3,
192-
offset: 12,
193-
shader_location: 0,
194-
},
195-
// Normal
196-
VertexAttribute {
197-
format: VertexFormat::Float32x3,
198-
offset: 0,
199-
shader_location: 1,
200-
},
201-
// Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically))
202-
VertexAttribute {
203-
format: VertexFormat::Float32x2,
204-
offset: 40,
205-
shader_location: 2,
206-
},
207-
// Tangent
208-
VertexAttribute {
209-
format: VertexFormat::Float32x4,
210-
offset: 24,
211-
shader_location: 3,
212-
},
213-
],
214-
)
215-
} else {
216-
(
217-
32,
218-
vec![
219-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
220-
VertexAttribute {
221-
format: VertexFormat::Float32x3,
222-
offset: 12,
223-
shader_location: 0,
224-
},
225-
// Normal
226-
VertexAttribute {
227-
format: VertexFormat::Float32x3,
228-
offset: 0,
229-
shader_location: 1,
230-
},
231-
// Uv
232-
VertexAttribute {
233-
format: VertexFormat::Float32x2,
234-
offset: 24,
235-
shader_location: 2,
236-
},
237-
],
238-
)
239-
};
183+
fn specialize(
184+
&self,
185+
_key: Self::Key,
186+
vertex_layout: &VertexBufferLayout,
187+
) -> RenderPipelineDescriptor {
240188
RenderPipelineDescriptor {
241189
vertex: VertexState {
242190
shader: SHADOW_SHADER_HANDLE.typed::<Shader>(),
243191
entry_point: "vertex".into(),
244192
shader_defs: vec![],
245-
buffers: vec![VertexBufferLayout {
246-
array_stride: vertex_array_stride,
247-
step_mode: VertexStepMode::Vertex,
248-
attributes: vertex_attributes,
249-
}],
193+
buffers: vec![vertex_layout.clone()],
250194
},
251195
fragment: None,
252196
layout: Some(vec![self.view_layout.clone(), self.mesh_layout.clone()]),
@@ -755,13 +699,16 @@ pub fn queue_shadows(
755699
for VisibleEntity { entity, .. } in visible_entities.iter() {
756700
let mut key = ShadowPipelineKey::empty();
757701
if let Ok(mesh_handle) = casting_meshes.get(*entity) {
758-
if let Some(mesh) = render_meshes.get(mesh_handle) {
759-
if mesh.has_tangents {
760-
key |= ShadowPipelineKey::VERTEX_TANGENTS;
761-
}
702+
let mesh = render_meshes.get(mesh_handle).unwrap();
703+
if mesh.has_tangents {
704+
key |= ShadowPipelineKey::VERTEX_TANGENTS;
762705
}
763-
let pipeline_id =
764-
pipelines.specialize(&mut pipeline_cache, &shadow_pipeline, key);
706+
let pipeline_id = pipelines.specialize(
707+
&mut pipeline_cache,
708+
&shadow_pipeline,
709+
key,
710+
&mesh.vertex_layout,
711+
);
765712

766713
shadow_phase.add(Shadow {
767714
draw_function: draw_shadow_mesh,

pipelined/bevy_pbr2/src/render/mod.rs

Lines changed: 15 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -470,63 +470,11 @@ impl PbrPipelineKey {
470470
impl SpecializedPipeline for PbrPipeline {
471471
type Key = PbrPipelineKey;
472472

473-
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
474-
let (vertex_array_stride, vertex_attributes) =
475-
if key.contains(PbrPipelineKey::VERTEX_TANGENTS) {
476-
(
477-
48,
478-
vec![
479-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
480-
VertexAttribute {
481-
format: VertexFormat::Float32x3,
482-
offset: 12,
483-
shader_location: 0,
484-
},
485-
// Normal
486-
VertexAttribute {
487-
format: VertexFormat::Float32x3,
488-
offset: 0,
489-
shader_location: 1,
490-
},
491-
// Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically))
492-
VertexAttribute {
493-
format: VertexFormat::Float32x2,
494-
offset: 40,
495-
shader_location: 2,
496-
},
497-
// Tangent
498-
VertexAttribute {
499-
format: VertexFormat::Float32x4,
500-
offset: 24,
501-
shader_location: 3,
502-
},
503-
],
504-
)
505-
} else {
506-
(
507-
32,
508-
vec![
509-
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
510-
VertexAttribute {
511-
format: VertexFormat::Float32x3,
512-
offset: 12,
513-
shader_location: 0,
514-
},
515-
// Normal
516-
VertexAttribute {
517-
format: VertexFormat::Float32x3,
518-
offset: 0,
519-
shader_location: 1,
520-
},
521-
// Uv
522-
VertexAttribute {
523-
format: VertexFormat::Float32x2,
524-
offset: 24,
525-
shader_location: 2,
526-
},
527-
],
528-
)
529-
};
473+
fn specialize(
474+
&self,
475+
key: Self::Key,
476+
vertex_layout: &VertexBufferLayout,
477+
) -> RenderPipelineDescriptor {
530478
let mut shader_defs = Vec::new();
531479
if key.contains(PbrPipelineKey::VERTEX_TANGENTS) {
532480
shader_defs.push(String::from("VERTEX_TANGENTS"));
@@ -554,11 +502,7 @@ impl SpecializedPipeline for PbrPipeline {
554502
shader: PBR_SHADER_HANDLE.typed::<Shader>(),
555503
entry_point: "vertex".into(),
556504
shader_defs: shader_defs.clone(),
557-
buffers: vec![VertexBufferLayout {
558-
array_stride: vertex_array_stride,
559-
step_mode: VertexStepMode::Vertex,
560-
attributes: vertex_attributes,
561-
}],
505+
buffers: vec![vertex_layout.clone()],
562506
},
563507
fragment: Some(FragmentState {
564508
shader: PBR_SHADER_HANDLE.typed::<Shader>(),
@@ -746,17 +690,21 @@ pub fn queue_meshes(
746690
} else {
747691
continue;
748692
};
749-
if let Some(mesh) = render_meshes.get(mesh_handle) {
750-
if mesh.has_tangents {
751-
key |= PbrPipelineKey::VERTEX_TANGENTS;
752-
}
693+
let mesh = render_meshes.get(mesh_handle).unwrap();
694+
if mesh.has_tangents {
695+
key |= PbrPipelineKey::VERTEX_TANGENTS;
753696
}
754697
key |= match alpha_mode {
755698
AlphaMode::Opaque => PbrPipelineKey::OPAQUE_MAIN_PASS,
756699
AlphaMode::Mask(_) => PbrPipelineKey::ALPHA_MASK_MAIN_PASS,
757700
AlphaMode::Blend => PbrPipelineKey::TRANSPARENT_MAIN_PASS,
758701
};
759-
let pipeline_id = pipelines.specialize(&mut pipeline_cache, &pbr_pipeline, key);
702+
let pipeline_id = pipelines.specialize(
703+
&mut pipeline_cache,
704+
&pbr_pipeline,
705+
key,
706+
&mesh.vertex_layout,
707+
);
760708

761709
// NOTE: row 2 of the inverse view matrix dotted with column 3 of the model matrix
762710
// gives the z component of translation of the mesh in view space

0 commit comments

Comments
 (0)