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` caches the vertex buffer
  layout and adds a cache key to the `GpuMesh` render asset. The
  `MeshPipeline` can lookup the vertex buffer layout by converting the
  specialization key to a vertex layout key..
- `SpritePipeline` populates the vertex layout cache for itself.
- `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
  • Loading branch information
parasyte committed Nov 25, 2021
1 parent 44ebcf8 commit 3a01381
Show file tree
Hide file tree
Showing 11 changed files with 406 additions and 223 deletions.
4 changes: 2 additions & 2 deletions examples/shader/custom_shader_pipelined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ pub struct CustomPipeline {
impl SpecializedPipeline for CustomPipeline {
type Key = MeshPipelineKey;

fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let mut descriptor = self.mesh_pipeline.specialize(key);
fn specialize(&self, cache: &RenderPipelineCache, key: Self::Key) -> RenderPipelineDescriptor {
let mut descriptor = self.mesh_pipeline.specialize(cache, key);
descriptor.fragment.as_mut().unwrap().shader = self.shader.clone();
descriptor.layout = Some(vec![
self.mesh_pipeline.view_layout.clone(),
Expand Down
17 changes: 11 additions & 6 deletions examples/shader/shader_defs_pipelined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,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 +96,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 @@ -105,20 +105,24 @@ impl FromWorld for IsRedPipeline {
impl SpecializedPipeline for IsRedPipeline {
type Key = (IsRed, MeshPipelineKey);

fn specialize(&self, (is_red, pbr_pipeline_key): Self::Key) -> RenderPipelineDescriptor {
fn specialize(
&self,
cache: &RenderPipelineCache,
(is_red, pbr_pipeline_key): Self::Key,
) -> RenderPipelineDescriptor {
let mut shader_defs = Vec::new();
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(cache, 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,6 +135,7 @@ type DrawIsRed = (
DrawMesh,
);

#[allow(clippy::too_many_arguments)]
fn queue_custom(
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
custom_pipeline: Res<IsRedPipeline>,
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
116 changes: 47 additions & 69 deletions pipelined/bevy_pbr2/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use bevy_math::{const_vec3, Mat4, Vec3, Vec4};
use bevy_render2::{
camera::CameraProjection,
color::Color,
mesh::Mesh,
mesh::{Mesh, VertexLayoutKey, VertexLayoutMeshKey},
render_asset::RenderAssets,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{
Expand Down Expand Up @@ -201,79 +201,60 @@ bitflags::bitflags! {
pub struct ShadowPipelineKey: u32 {
const NONE = 0;
const VERTEX_TANGENTS = (1 << 0);
const VERTEX_JOINTS = (1 << 1);
}
}

impl From<ShadowPipelineKey> for VertexLayoutKey {
fn from(value: ShadowPipelineKey) -> Self {
let mut key = VertexLayoutMeshKey::empty();

if value.contains(ShadowPipelineKey::VERTEX_TANGENTS) {
key |= VertexLayoutMeshKey::TANGENTS;
}
if value.contains(ShadowPipelineKey::VERTEX_JOINTS) {
key |= VertexLayoutMeshKey::JOINTS;
}

Self::Mesh(key)
}
}

impl From<VertexLayoutKey> for ShadowPipelineKey {
fn from(value: VertexLayoutKey) -> Self {
match value {
VertexLayoutKey::Mesh(mesh_key) => {
let mut key = Self::empty();

if mesh_key.contains(VertexLayoutMeshKey::TANGENTS) {
key |= Self::VERTEX_TANGENTS;
}
if mesh_key.contains(VertexLayoutMeshKey::JOINTS) {
key |= Self::VERTEX_JOINTS;
}

key
}
_ => panic!("Invalid vertex layout key"),
}
}
}

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, cache: &RenderPipelineCache, key: Self::Key) -> RenderPipelineDescriptor {
let vertex_layout = cache
.vertex_layout_cache
.get(&VertexLayoutKey::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![vertex_layout.clone()],
},
fragment: None,
layout: Some(vec![self.view_layout.clone(), self.mesh_layout.clone()]),
Expand Down Expand Up @@ -815,13 +796,10 @@ pub fn queue_shadows(
// NOTE: Lights with shadow mapping disabled will have no visible entities
// so no meshes will be queued
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();
let key = ShadowPipelineKey::from(mesh.vertex_layout_key);

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

Expand Down
109 changes: 45 additions & 64 deletions pipelined/bevy_pbr2/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use bevy_ecs::{
use bevy_math::Mat4;
use bevy_reflect::TypeUuid;
use bevy_render2::{
mesh::Mesh,
mesh::{Mesh, VertexLayoutKey, VertexLayoutMeshKey},
render_asset::RenderAssets,
render_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
Expand Down Expand Up @@ -328,11 +328,47 @@ bitflags::bitflags! {
pub struct MeshPipelineKey: u32 {
const NONE = 0;
const VERTEX_TANGENTS = (1 << 0);
const TRANSPARENT_MAIN_PASS = (1 << 1);
const VERTEX_JOINTS = (1 << 1);
const TRANSPARENT_MAIN_PASS = (1 << 2);
const MSAA_RESERVED_BITS = MeshPipelineKey::MSAA_MASK_BITS << MeshPipelineKey::MSAA_SHIFT_BITS;
}
}

impl From<MeshPipelineKey> for VertexLayoutKey {
fn from(value: MeshPipelineKey) -> Self {
let mut key = VertexLayoutMeshKey::empty();

if value.contains(MeshPipelineKey::VERTEX_TANGENTS) {
key |= VertexLayoutMeshKey::TANGENTS;
}
if value.contains(MeshPipelineKey::VERTEX_JOINTS) {
key |= VertexLayoutMeshKey::JOINTS;
}

Self::Mesh(key)
}
}

impl From<VertexLayoutKey> for MeshPipelineKey {
fn from(value: VertexLayoutKey) -> Self {
match value {
VertexLayoutKey::Mesh(mesh_key) => {
let mut key = Self::empty();

if mesh_key.contains(VertexLayoutMeshKey::TANGENTS) {
key |= Self::VERTEX_TANGENTS;
}
if mesh_key.contains(VertexLayoutMeshKey::JOINTS) {
key |= Self::VERTEX_JOINTS;
}

key
}
_ => panic!("Invalid vertex layout key"),
}
}
}

impl MeshPipelineKey {
const MSAA_MASK_BITS: u32 = 0b111111;
const MSAA_SHIFT_BITS: u32 = 32 - 6;
Expand All @@ -350,63 +386,12 @@ impl MeshPipelineKey {
impl SpecializedPipeline for MeshPipeline {
type Key = MeshPipelineKey;

fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let (vertex_array_stride, vertex_attributes) =
if key.contains(MeshPipelineKey::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, cache: &RenderPipelineCache, key: Self::Key) -> RenderPipelineDescriptor {
let vertex_layout = cache
.vertex_layout_cache
.get(&VertexLayoutKey::from(key))
.unwrap();

let mut shader_defs = Vec::new();
if key.contains(MeshPipelineKey::VERTEX_TANGENTS) {
shader_defs.push(String::from("VERTEX_TANGENTS"));
Expand All @@ -433,11 +418,7 @@ impl SpecializedPipeline for MeshPipeline {
shader: MESH_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: MESH_SHADER_HANDLE.typed::<Shader>(),
Expand Down
11 changes: 4 additions & 7 deletions pipelined/bevy_pbr2/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ pub struct PbrPipelineKey {
impl SpecializedPipeline for PbrPipeline {
type Key = PbrPipelineKey;

fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key);
fn specialize(&self, cache: &RenderPipelineCache, key: Self::Key) -> RenderPipelineDescriptor {
let mut descriptor = self.mesh_pipeline.specialize(cache, key.mesh_key);
descriptor.fragment.as_mut().unwrap().shader = PBR_SHADER_HANDLE.typed::<Shader>();
descriptor.layout = Some(vec![
self.mesh_pipeline.view_layout.clone(),
Expand Down Expand Up @@ -256,11 +256,8 @@ pub fn queue_meshes(
mesh_key,
normal_map: material.has_normal_map,
};
if let Some(mesh) = render_meshes.get(mesh_handle) {
if mesh.has_tangents {
pbr_key.mesh_key |= MeshPipelineKey::VERTEX_TANGENTS;
}
}
let mesh = render_meshes.get(mesh_handle).unwrap();
pbr_key.mesh_key |= MeshPipelineKey::from(mesh.vertex_layout_key);

if let AlphaMode::Blend = material.alpha_mode {
pbr_key.mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS
Expand Down
Loading

0 comments on commit 3a01381

Please sign in to comment.