Skip to content

Commit

Permalink
Add dynamic vertex buffer layouts to resolve sorting issues
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
parasyte committed Nov 15, 2021
1 parent 9a4cc42 commit bafe868
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 212 deletions.
15 changes: 11 additions & 4 deletions examples/shader/custom_shader_pipelined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,12 @@ pub struct CustomPipeline {
impl SpecializedPipeline for CustomPipeline {
type Key = PbrPipelineKey;

fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let mut descriptor = self.pbr_pipeline.specialize(key);
fn specialize(
&self,
key: Self::Key,
vertex_layout: &VertexBufferLayout,
) -> RenderPipelineDescriptor {
let mut descriptor = self.pbr_pipeline.specialize(key, vertex_layout);
descriptor.vertex.shader = self.shader.clone();
descriptor.fragment.as_mut().unwrap().shader = self.shader.clone();
descriptor.layout = Some(vec![
Expand Down Expand Up @@ -178,11 +182,12 @@ impl FromWorld for CustomPipeline {
pub fn queue_custom(
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
materials: Res<RenderAssets<CustomMaterial>>,
render_meshes: Res<RenderAssets<Mesh>>,
custom_pipeline: Res<CustomPipeline>,
mut pipeline_cache: ResMut<RenderPipelineCache>,
mut specialized_pipelines: ResMut<SpecializedPipelines<CustomPipeline>>,
msaa: Res<Msaa>,
material_meshes: Query<(Entity, &Handle<CustomMaterial>, &MeshUniform), With<Handle<Mesh>>>,
material_meshes: Query<(Entity, &Handle<CustomMaterial>, &Handle<Mesh>, &MeshUniform)>,
mut views: Query<(&ExtractedView, &mut RenderPhase<Transparent3d>)>,
) {
let draw_custom = transparent_3d_draw_functions
Expand All @@ -193,14 +198,16 @@ pub fn queue_custom(
for (view, mut transparent_phase) in views.iter_mut() {
let view_matrix = view.transform.compute_matrix();
let view_row_2 = view_matrix.row(2);
for (entity, material_handle, mesh_uniform) in material_meshes.iter() {
for (entity, material_handle, mesh_handle, mesh_uniform) in material_meshes.iter() {
if materials.contains_key(material_handle) {
let mesh = render_meshes.get(mesh_handle).unwrap();
transparent_phase.add(Transparent3d {
entity,
pipeline: specialized_pipelines.specialize(
&mut pipeline_cache,
&custom_pipeline,
key,
&mesh.vertex_layout,
),
draw_function: draw_custom,
distance: view_row_2.dot(mesh_uniform.transform.col(3)),
Expand Down
26 changes: 20 additions & 6 deletions examples/shader/shader_defs_pipelined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use bevy::{
render2::{
camera::PerspectiveCameraBundle,
mesh::{shape, Mesh},
render_asset::RenderAssets,
render_component::{ExtractComponent, ExtractComponentPlugin},
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
render_resource::*,
Expand Down Expand Up @@ -105,12 +106,18 @@ impl FromWorld for IsRedPipeline {
impl SpecializedPipeline for IsRedPipeline {
type Key = (IsRed, PbrPipelineKey);

fn specialize(&self, (is_red, pbr_pipeline_key): Self::Key) -> RenderPipelineDescriptor {
fn specialize(
&self,
(is_red, pbr_pipeline_key): Self::Key,
vertex_layout: &VertexBufferLayout,
) -> RenderPipelineDescriptor {
let mut shader_defs = Vec::new();
if is_red.0 {
shader_defs.push("IS_RED".to_string());
}
let mut descriptor = self.pbr_pipeline.specialize(pbr_pipeline_key);
let mut descriptor = self
.pbr_pipeline
.specialize(pbr_pipeline_key, vertex_layout);
descriptor.vertex.shader = self.shader.clone();
descriptor.vertex.shader_defs = shader_defs.clone();
let fragment = descriptor.fragment.as_mut().unwrap();
Expand All @@ -131,13 +138,15 @@ type DrawIsRed = (
DrawMesh,
);

#[allow(clippy::too_many_arguments)]
fn queue_custom(
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
custom_pipeline: Res<IsRedPipeline>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>,
mut pipelines: ResMut<SpecializedPipelines<IsRedPipeline>>,
mut pipeline_cache: ResMut<RenderPipelineCache>,
material_meshes: Query<(Entity, &MeshUniform, &IsRed), With<Handle<Mesh>>>,
material_meshes: Query<(Entity, &Handle<Mesh>, &MeshUniform, &IsRed)>,
mut views: Query<(&ExtractedView, &mut RenderPhase<Transparent3d>)>,
) {
let draw_custom = transparent_3d_draw_functions
Expand All @@ -148,9 +157,14 @@ fn queue_custom(
for (view, mut transparent_phase) in views.iter_mut() {
let view_matrix = view.transform.compute_matrix();
let view_row_2 = view_matrix.row(2);
for (entity, mesh_uniform, is_red) in material_meshes.iter() {
let pipeline =
pipelines.specialize(&mut pipeline_cache, &custom_pipeline, (*is_red, key));
for (entity, mesh_handle, mesh_uniform, is_red) in material_meshes.iter() {
let mesh = render_meshes.get(mesh_handle).unwrap();
let pipeline = pipelines.specialize(
&mut pipeline_cache,
&custom_pipeline,
(*is_red, key),
&mesh.vertex_layout,
);
transparent_phase.add(Transparent3d {
entity,
pipeline,
Expand Down
3 changes: 3 additions & 0 deletions pipelined/bevy_gltf2/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use bevy_render2::{
color::Color,
mesh::{Indices, Mesh, VertexAttributeValues},
primitives::Aabb,
render_resource::VertexFormat,
texture::{Image, ImageType, TextureError},
};
use bevy_scene::Scene;
Expand Down Expand Up @@ -133,6 +134,8 @@ async fn load_gltf<'a, 'b>(
.read_tangents()
.map(|v| VertexAttributeValues::Float32x4(v.collect()))
{
mesh.vertex_layout_mut()
.push(Mesh::ATTRIBUTE_TANGENT, VertexFormat::Float32x4);
mesh.set_attribute(Mesh::ATTRIBUTE_TANGENT, vertex_attribute);
}

Expand Down
83 changes: 15 additions & 68 deletions pipelined/bevy_pbr2/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,73 +180,17 @@ bitflags::bitflags! {
impl SpecializedPipeline for ShadowPipeline {
type Key = ShadowPipelineKey;

fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let (vertex_array_stride, vertex_attributes) =
if key.contains(ShadowPipelineKey::VERTEX_TANGENTS) {
(
48,
vec![
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
// Normal
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
// Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 40,
shader_location: 2,
},
// Tangent
VertexAttribute {
format: VertexFormat::Float32x4,
offset: 24,
shader_location: 3,
},
],
)
} else {
(
32,
vec![
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
// Normal
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
// Uv
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
},
],
)
};
fn specialize(
&self,
_key: Self::Key,
vertex_layout: &VertexBufferLayout,
) -> RenderPipelineDescriptor {
RenderPipelineDescriptor {
vertex: VertexState {
shader: SHADOW_SHADER_HANDLE.typed::<Shader>(),
entry_point: "vertex".into(),
shader_defs: vec![],
buffers: vec![VertexBufferLayout {
array_stride: vertex_array_stride,
step_mode: VertexStepMode::Vertex,
attributes: vertex_attributes,
}],
buffers: vec![vertex_layout.clone()],
},
fragment: None,
layout: Some(vec![self.view_layout.clone(), self.mesh_layout.clone()]),
Expand Down Expand Up @@ -755,13 +699,16 @@ pub fn queue_shadows(
for VisibleEntity { entity, .. } in visible_entities.iter() {
let mut key = ShadowPipelineKey::empty();
if let Ok(mesh_handle) = casting_meshes.get(*entity) {
if let Some(mesh) = render_meshes.get(mesh_handle) {
if mesh.has_tangents {
key |= ShadowPipelineKey::VERTEX_TANGENTS;
}
let mesh = render_meshes.get(mesh_handle).unwrap();
if mesh.has_tangents {
key |= ShadowPipelineKey::VERTEX_TANGENTS;
}
let pipeline_id =
pipelines.specialize(&mut pipeline_cache, &shadow_pipeline, key);
let pipeline_id = pipelines.specialize(
&mut pipeline_cache,
&shadow_pipeline,
key,
&mesh.vertex_layout,
);

shadow_phase.add(Shadow {
draw_function: draw_shadow_mesh,
Expand Down
82 changes: 15 additions & 67 deletions pipelined/bevy_pbr2/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,63 +447,11 @@ impl PbrPipelineKey {
impl SpecializedPipeline for PbrPipeline {
type Key = PbrPipelineKey;

fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let (vertex_array_stride, vertex_attributes) =
if key.contains(PbrPipelineKey::VERTEX_TANGENTS) {
(
48,
vec![
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
// Normal
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
// Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 40,
shader_location: 2,
},
// Tangent
VertexAttribute {
format: VertexFormat::Float32x4,
offset: 24,
shader_location: 3,
},
],
)
} else {
(
32,
vec![
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
// Normal
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
// Uv
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
},
],
)
};
fn specialize(
&self,
key: Self::Key,
vertex_layout: &VertexBufferLayout,
) -> RenderPipelineDescriptor {
let mut shader_defs = Vec::new();
if key.contains(PbrPipelineKey::VERTEX_TANGENTS) {
shader_defs.push(String::from("VERTEX_TANGENTS"));
Expand All @@ -516,11 +464,7 @@ impl SpecializedPipeline for PbrPipeline {
shader: PBR_SHADER_HANDLE.typed::<Shader>(),
entry_point: "vertex".into(),
shader_defs: shader_defs.clone(),
buffers: vec![VertexBufferLayout {
array_stride: vertex_array_stride,
step_mode: VertexStepMode::Vertex,
attributes: vertex_attributes,
}],
buffers: vec![vertex_layout.clone()],
},
fragment: Some(FragmentState {
shader: PBR_SHADER_HANDLE.typed::<Shader>(),
Expand Down Expand Up @@ -704,12 +648,16 @@ pub fn queue_meshes(
} else {
continue;
}
if let Some(mesh) = render_meshes.get(mesh_handle) {
if mesh.has_tangents {
key |= PbrPipelineKey::VERTEX_TANGENTS;
}
let mesh = render_meshes.get(mesh_handle).unwrap();
if mesh.has_tangents {
key |= PbrPipelineKey::VERTEX_TANGENTS;
}
let pipeline_id = pipelines.specialize(&mut pipeline_cache, &pbr_pipeline, key);
let pipeline_id = pipelines.specialize(
&mut pipeline_cache,
&pbr_pipeline,
key,
&mesh.vertex_layout,
);

// NOTE: row 2 of the view matrix dotted with column 3 of the model matrix
// gives the z component of translation of the mesh in view space
Expand Down
Loading

0 comments on commit bafe868

Please sign in to comment.