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 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` copies the vertex buffer
  layout to the `GpuMesh` render asset. The `MeshPipeline` has a cache for
  vertex layouts.
- Any pipeline that extends `MeshPipeline` needs to carefully populate its
  vertex layout cache before it can be specialized.
- `SpritePipeline` has its own vertex layout cache, but these are
  hardcoded.
- Discussion: https://discord.com/channels/691052431525675048/743663924229963868/908484759833960489
  • Loading branch information
parasyte committed Nov 24, 2021
1 parent 85c37e6 commit 3e3c18d
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 211 deletions.
13 changes: 10 additions & 3 deletions examples/shader/custom_shader_pipelined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,12 @@ impl FromWorld for CustomPipeline {
pub fn queue_custom(
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
materials: Res<RenderAssets<CustomMaterial>>,
custom_pipeline: Res<CustomPipeline>,
render_meshes: Res<RenderAssets<Mesh>>,
mut custom_pipeline: ResMut<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 @@ -192,8 +193,14 @@ 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) {
// Vertex layout must be cached by MeshPipeline before it can be specialized
let mesh = render_meshes.get(mesh_handle).unwrap();
custom_pipeline
.mesh_pipeline
.cache_vertex_layout(key, &mesh.vertex_layout);

transparent_phase.add(Transparent3d {
entity,
pipeline: specialized_pipelines.specialize(
Expand Down
25 changes: 17 additions & 8 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 @@ -86,7 +87,7 @@ fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
}

struct IsRedPipeline {
mesh_pipline: MeshPipeline,
mesh_pipeline: MeshPipeline,
shader: Handle<Shader>,
}

Expand All @@ -96,7 +97,7 @@ impl FromWorld for IsRedPipeline {
let mesh_pipeline = world.get_resource::<MeshPipeline>().unwrap();
let shader = asset_server.load("shaders/shader_defs.wgsl");
IsRedPipeline {
mesh_pipline: mesh_pipeline.clone(),
mesh_pipeline: mesh_pipeline.clone(),
shader,
}
}
Expand All @@ -110,15 +111,15 @@ impl SpecializedPipeline for IsRedPipeline {
if is_red.0 {
shader_defs.push("IS_RED".to_string());
}
let mut descriptor = self.mesh_pipline.specialize(pbr_pipeline_key);
let mut descriptor = self.mesh_pipeline.specialize(pbr_pipeline_key);
descriptor.vertex.shader = self.shader.clone();
descriptor.vertex.shader_defs = shader_defs.clone();
let fragment = descriptor.fragment.as_mut().unwrap();
fragment.shader = self.shader.clone();
fragment.shader_defs = shader_defs;
descriptor.layout = Some(vec![
self.mesh_pipline.view_layout.clone(),
self.mesh_pipline.mesh_layout.clone(),
self.mesh_pipeline.view_layout.clone(),
self.mesh_pipeline.mesh_layout.clone(),
]);
descriptor
}
Expand All @@ -131,13 +132,15 @@ type DrawIsRed = (
DrawMesh,
);

#[allow(clippy::too_many_arguments)]
fn queue_custom(
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
custom_pipeline: Res<IsRedPipeline>,
mut custom_pipeline: ResMut<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,7 +151,13 @@ 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() {
for (entity, mesh_handle, mesh_uniform, is_red) in material_meshes.iter() {
// Vertex layout must be cached by MeshPipeline before it can be specialized
let mesh = render_meshes.get(mesh_handle).unwrap();
custom_pipeline
.mesh_pipeline
.cache_vertex_layout(key, &mesh.vertex_layout);

let pipeline =
pipelines.specialize(&mut pipeline_cache, &custom_pipeline, (*is_red, key));
transparent_phase.add(Transparent3d {
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 @@ -132,6 +133,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
106 changes: 35 additions & 71 deletions pipelined/bevy_pbr2/src/render/light.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
AmbientLight, CubemapVisibleEntities, DirectionalLight, DirectionalLightShadowMap, DrawMesh,
MeshPipeline, NotShadowCaster, PointLight, PointLightShadowMap, SetMeshBindGroup,
SHADOW_SHADER_HANDLE,
MeshPipeline, MeshPipelineKey, NotShadowCaster, PointLight, PointLightShadowMap,
SetMeshBindGroup, SHADOW_SHADER_HANDLE,
};
use bevy_asset::Handle;
use bevy_core::FloatOrd;
Expand Down Expand Up @@ -137,7 +137,7 @@ pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float;

pub struct ShadowPipeline {
pub view_layout: BindGroupLayout,
pub mesh_layout: BindGroupLayout,
pub mesh_pipeline: MeshPipeline,
pub point_light_sampler: Sampler,
pub directional_light_sampler: Sampler,
}
Expand Down Expand Up @@ -169,7 +169,7 @@ impl FromWorld for ShadowPipeline {

ShadowPipeline {
view_layout,
mesh_layout: mesh_pipeline.mesh_layout.clone(),
mesh_pipeline: mesh_pipeline.clone(),
point_light_sampler: render_device.create_sampler(&SamplerDescriptor {
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
Expand Down Expand Up @@ -202,79 +202,38 @@ bitflags::bitflags! {
}
}

impl From<ShadowPipelineKey> for MeshPipelineKey {
fn from(value: ShadowPipelineKey) -> Self {
if value & ShadowPipelineKey::VERTEX_TANGENTS == ShadowPipelineKey::VERTEX_TANGENTS {
Self::VERTEX_TANGENTS
} else {
Self::NONE
}
}
}

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,
},
],
)
};
let vertex_layout = self
.mesh_pipeline
.vertex_layouts
.get(&MeshPipelineKey::from(key))
.unwrap();

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![dbg!(vertex_layout).clone()],
},
fragment: None,
layout: Some(vec![self.view_layout.clone(), self.mesh_layout.clone()]),
layout: Some(vec![
self.view_layout.clone(),
self.mesh_pipeline.mesh_layout.clone(),
]),
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
Expand Down Expand Up @@ -775,7 +734,7 @@ pub fn queue_shadow_view_bind_group(
#[allow(clippy::too_many_arguments)]
pub fn queue_shadows(
shadow_draw_functions: Res<DrawFunctions<Shadow>>,
shadow_pipeline: Res<ShadowPipeline>,
mut shadow_pipeline: ResMut<ShadowPipeline>,
casting_meshes: Query<&Handle<Mesh>, Without<NotShadowCaster>>,
render_meshes: Res<RenderAssets<Mesh>>,
mut pipelines: ResMut<SpecializedPipelines<ShadowPipeline>>,
Expand Down Expand Up @@ -810,11 +769,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;
}

// Vertex layout must be cached by MeshPipeline before it can be specialized
shadow_pipeline
.mesh_pipeline
.cache_vertex_layout(MeshPipelineKey::from(key), dbg!(&mesh.vertex_layout));

let pipeline_id =
pipelines.specialize(&mut pipeline_cache, &shadow_pipeline, key);

Expand Down
Loading

0 comments on commit 3e3c18d

Please sign in to comment.