bevy/pipelined/bevy_pbr2/src/render/mod.rs

516 lines
18 KiB
Rust
Raw Normal View History

2021-06-02 02:59:17 +00:00
mod light;
pub use light::*;
use bevy_asset::{Assets, Handle};
use bevy_ecs::{prelude::*, system::SystemState};
use bevy_math::Mat4;
use bevy_render2::{
core_pipeline::Transparent3dPhase,
mesh::Mesh,
render_asset::RenderAssets,
2021-06-02 02:59:17 +00:00
render_graph::{Node, NodeRunError, RenderGraphContext},
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
2021-06-21 23:28:52 +00:00
render_resource::*,
renderer::{RenderContext, RenderDevice},
shader::Shader,
texture::BevyDefault,
view::{ViewMeta, ViewUniform, ViewUniformOffset},
2021-06-02 02:59:17 +00:00
};
use bevy_transform::components::GlobalTransform;
use bevy_utils::slab::{FrameSlabMap, FrameSlabMapKey};
use crevice::std140::AsStd140;
2021-06-21 23:28:52 +00:00
use std::borrow::Cow;
use crate::{StandardMaterial, StandardMaterialUniformData};
2021-06-02 02:59:17 +00:00
pub struct PbrShaders {
2021-06-21 23:28:52 +00:00
pipeline: RenderPipeline,
vertex_shader_module: ShaderModule,
view_layout: BindGroupLayout,
material_layout: BindGroupLayout,
mesh_layout: BindGroupLayout,
2021-06-02 02:59:17 +00:00
}
// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
impl FromWorld for PbrShaders {
fn from_world(world: &mut World) -> Self {
2021-06-21 23:28:52 +00:00
let render_device = world.get_resource::<RenderDevice>().unwrap();
let vertex_shader = Shader::from_glsl(ShaderStage::VERTEX, include_str!("pbr.vert"))
2021-06-02 02:59:17 +00:00
.get_spirv_shader(None)
.unwrap();
2021-06-21 23:28:52 +00:00
let fragment_shader = Shader::from_glsl(ShaderStage::FRAGMENT, include_str!("pbr.frag"))
2021-06-02 02:59:17 +00:00
.get_spirv_shader(None)
.unwrap();
2021-06-21 23:28:52 +00:00
let vertex_spirv = vertex_shader.get_spirv(None).unwrap();
let fragment_spirv = fragment_shader.get_spirv(None).unwrap();
let vertex_shader_module = render_device.create_shader_module(&ShaderModuleDescriptor {
flags: ShaderFlags::default(),
label: None,
source: ShaderSource::SpirV(Cow::Borrowed(&vertex_spirv)),
});
let fragment_shader_module = render_device.create_shader_module(&ShaderModuleDescriptor {
flags: ShaderFlags::default(),
label: None,
source: ShaderSource::SpirV(Cow::Borrowed(&fragment_spirv)),
});
// TODO: move this into ViewMeta?
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
// View
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStage::VERTEX | ShaderStage::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
// TODO: verify this is correct
min_binding_size: BufferSize::new(ViewUniform::std140_size_static() as u64),
},
count: None,
2021-06-02 02:59:17 +00:00
},
2021-06-21 23:28:52 +00:00
// Lights
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStage::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: BufferSize::new(GpuLights::std140_size_static() as u64),
},
count: None,
2021-06-02 02:59:17 +00:00
},
2021-06-21 23:28:52 +00:00
// Shadow Texture Array
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStage::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Depth,
view_dimension: TextureViewDimension::D2Array,
},
count: None,
},
// Shadow Texture Array Sampler
BindGroupLayoutEntry {
binding: 3,
visibility: ShaderStage::FRAGMENT,
ty: BindingType::Sampler {
comparison: true,
filtering: true,
},
count: None,
2021-06-02 02:59:17 +00:00
},
],
2021-06-21 23:28:52 +00:00
label: None,
});
let mesh_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStage::VERTEX,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: BufferSize::new(Mat4::std140_size_static() as u64),
},
count: None,
}],
label: None,
});
2021-06-02 02:59:17 +00:00
2021-06-21 23:28:52 +00:00
let material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStage::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: BufferSize::new(
StandardMaterialUniformData::std140_size_static() as u64,
),
},
count: None,
}],
label: None,
});
2021-06-02 02:59:17 +00:00
2021-06-21 23:28:52 +00:00
let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
push_constant_ranges: &[],
bind_group_layouts: &[&view_layout, &mesh_layout, &material_layout],
});
let pipeline = render_device.create_render_pipeline(&RenderPipelineDescriptor {
label: None,
vertex: VertexState {
buffers: &[VertexBufferLayout {
array_stride: 32,
step_mode: InputStepMode::Vertex,
attributes: &[
// 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,
},
],
}],
module: &&vertex_shader_module,
entry_point: "main",
},
fragment: Some(FragmentState {
module: &&fragment_shader_module,
entry_point: "main",
targets: &[ColorTargetState {
format: TextureFormat::bevy_default(),
blend: Some(BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
}),
write_mask: ColorWrite::ALL,
}],
}),
2021-06-02 02:59:17 +00:00
depth_stencil: Some(DepthStencilState {
format: TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: CompareFunction::Less,
stencil: StencilState {
front: StencilFaceState::IGNORE,
back: StencilFaceState::IGNORE,
read_mask: 0,
write_mask: 0,
},
bias: DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
2021-06-21 23:28:52 +00:00
layout: Some(&pipeline_layout),
multisample: MultisampleState::default(),
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Ccw,
cull_mode: Some(Face::Back),
polygon_mode: PolygonMode::Fill,
clamp_depth: false,
conservative: false,
},
});
2021-06-02 02:59:17 +00:00
PbrShaders {
pipeline,
2021-06-21 23:28:52 +00:00
view_layout,
material_layout,
mesh_layout,
vertex_shader_module,
2021-06-02 02:59:17 +00:00
}
}
}
struct ExtractedMesh {
transform: Mat4,
mesh: Handle<Mesh>,
2021-06-21 23:28:52 +00:00
material_buffer: Buffer,
transform_binding_offset: u32,
2021-06-02 02:59:17 +00:00
}
pub struct ExtractedMeshes {
meshes: Vec<ExtractedMesh>,
}
pub fn extract_meshes(
mut commands: Commands,
meshes: Res<Assets<Mesh>>,
materials: Res<Assets<StandardMaterial>>,
2021-06-02 02:59:17 +00:00
query: Query<(&GlobalTransform, &Handle<Mesh>, &Handle<StandardMaterial>)>,
) {
let mut extracted_meshes = Vec::new();
for (transform, mesh_handle, material_handle) in query.iter() {
if !meshes.contains(mesh_handle) {
continue;
}
if let Some(material) = materials.get(material_handle) {
if let Some(material_gpu_data) = &material.gpu_data() {
extracted_meshes.push(ExtractedMesh {
transform: transform.compute_matrix(),
mesh: mesh_handle.clone_weak(),
material_buffer: material_gpu_data.buffer.clone(),
transform_binding_offset: 0,
});
2021-06-02 02:59:17 +00:00
}
}
}
commands.insert_resource(ExtractedMeshes {
meshes: extracted_meshes,
});
}
struct MeshDrawInfo {
// TODO: compare cost of doing this vs cloning the BindGroup?
material_bind_group_key: FrameSlabMapKey<BufferId, BindGroup>,
}
2021-06-02 02:59:17 +00:00
#[derive(Default)]
pub struct MeshMeta {
transform_uniforms: DynamicUniformVec<Mat4>,
material_bind_groups: FrameSlabMap<BufferId, BindGroup>,
2021-06-21 23:28:52 +00:00
mesh_transform_bind_group: Option<BindGroup>,
mesh_draw_info: Vec<MeshDrawInfo>,
2021-06-02 02:59:17 +00:00
}
pub fn prepare_meshes(
2021-06-21 23:28:52 +00:00
render_device: Res<RenderDevice>,
2021-06-02 02:59:17 +00:00
mut mesh_meta: ResMut<MeshMeta>,
mut extracted_meshes: ResMut<ExtractedMeshes>,
) {
mesh_meta
.transform_uniforms
2021-06-21 23:28:52 +00:00
.reserve_and_clear(extracted_meshes.meshes.len(), &render_device);
2021-06-02 02:59:17 +00:00
for extracted_mesh in extracted_meshes.meshes.iter_mut() {
extracted_mesh.transform_binding_offset =
mesh_meta.transform_uniforms.push(extracted_mesh.transform);
}
mesh_meta
.transform_uniforms
2021-06-21 23:28:52 +00:00
.write_to_staging_buffer(&render_device);
2021-06-02 02:59:17 +00:00
}
2021-06-21 23:28:52 +00:00
pub struct MeshViewBindGroups {
view: BindGroup,
}
2021-06-02 02:59:17 +00:00
pub fn queue_meshes(
mut commands: Commands,
draw_functions: Res<DrawFunctions>,
2021-06-21 23:28:52 +00:00
render_device: Res<RenderDevice>,
2021-06-02 02:59:17 +00:00
pbr_shaders: Res<PbrShaders>,
shadow_shaders: Res<ShadowShaders>,
2021-06-21 23:28:52 +00:00
mesh_meta: ResMut<MeshMeta>,
mut light_meta: ResMut<LightMeta>,
2021-06-02 02:59:17 +00:00
view_meta: Res<ViewMeta>,
mut extracted_meshes: ResMut<ExtractedMeshes>,
2021-06-02 02:59:17 +00:00
mut views: Query<(Entity, &ViewLights, &mut RenderPhase<Transparent3dPhase>)>,
mut view_light_shadow_phases: Query<&mut RenderPhase<ShadowPhase>>,
) {
2021-06-21 23:28:52 +00:00
let mesh_meta = mesh_meta.into_inner();
light_meta.shadow_view_bind_group.get_or_insert_with(|| {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: view_meta.uniforms.binding(),
}],
label: None,
layout: &shadow_shaders.view_layout,
})
});
2021-06-02 02:59:17 +00:00
if extracted_meshes.meshes.is_empty() {
return;
}
2021-06-21 23:28:52 +00:00
let transform_uniforms = &mesh_meta.transform_uniforms;
mesh_meta.mesh_transform_bind_group.get_or_insert_with(|| {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: transform_uniforms.binding(),
}],
label: None,
layout: &pbr_shaders.mesh_layout,
})
});
2021-06-02 02:59:17 +00:00
for (entity, view_lights, mut transparent_phase) in views.iter_mut() {
2021-06-21 23:28:52 +00:00
// TODO: cache this?
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
BindGroupEntry {
binding: 0,
resource: view_meta.uniforms.binding(),
},
BindGroupEntry {
binding: 1,
resource: light_meta.view_gpu_lights.binding(),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::TextureView(&view_lights.light_depth_texture_view),
},
BindGroupEntry {
binding: 3,
resource: BindingResource::Sampler(&shadow_shaders.light_sampler),
},
],
label: None,
layout: &pbr_shaders.view_layout,
});
2021-06-02 02:59:17 +00:00
commands.entity(entity).insert(MeshViewBindGroups {
2021-06-21 23:28:52 +00:00
view: view_bind_group,
2021-06-02 02:59:17 +00:00
});
let draw_pbr = draw_functions.read().get_id::<DrawPbr>().unwrap();
mesh_meta.mesh_draw_info.clear();
mesh_meta.material_bind_groups.next_frame();
for (i, mesh) in extracted_meshes.meshes.iter_mut().enumerate() {
let material_bind_group_key = mesh_meta.material_bind_groups.get_or_insert_with(
mesh.material_buffer.id(),
|| {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: mesh.material_buffer.as_entire_binding(),
}],
label: None,
layout: &pbr_shaders.material_layout,
})
},
);
mesh_meta.mesh_draw_info.push(MeshDrawInfo {
material_bind_group_key,
});
2021-06-02 02:59:17 +00:00
// TODO: currently there is only "transparent phase". this should pick transparent vs opaque according to the mesh material
transparent_phase.add(Drawable {
draw_function: draw_pbr,
draw_key: i,
sort_key: material_bind_group_key.index(), // TODO: sort back-to-front, sorting by material for now
2021-06-02 02:59:17 +00:00
});
}
// ultimately lights should check meshes for relevancy (ex: light views can "see" different meshes than the main view can)
let draw_shadow_mesh = draw_functions.read().get_id::<DrawShadowMesh>().unwrap();
for view_light_entity in view_lights.lights.iter().copied() {
let mut shadow_phase = view_light_shadow_phases.get_mut(view_light_entity).unwrap();
// TODO: this should only queue up meshes that are actually visible by each "light view"
for i in 0..extracted_meshes.meshes.len() {
shadow_phase.add(Drawable {
draw_function: draw_shadow_mesh,
draw_key: i,
sort_key: 0, // TODO: sort back-to-front
})
}
}
}
}
// TODO: this logic can be moved to prepare_meshes once wgpu::Queue is exposed directly
pub struct PbrNode;
impl Node for PbrNode {
fn run(
&self,
_graph: &mut RenderGraphContext,
2021-06-21 23:28:52 +00:00
render_context: &mut RenderContext,
2021-06-02 02:59:17 +00:00
world: &World,
) -> Result<(), NodeRunError> {
let mesh_meta = world.get_resource::<MeshMeta>().unwrap();
let light_meta = world.get_resource::<LightMeta>().unwrap();
mesh_meta
.transform_uniforms
2021-06-21 23:28:52 +00:00
.write_to_uniform_buffer(&mut render_context.command_encoder);
2021-06-02 02:59:17 +00:00
light_meta
.view_gpu_lights
2021-06-21 23:28:52 +00:00
.write_to_uniform_buffer(&mut render_context.command_encoder);
2021-06-02 02:59:17 +00:00
Ok(())
}
}
2021-06-21 23:28:52 +00:00
type DrawPbrParams<'s, 'w> = (
Res<'w, PbrShaders>,
Res<'w, MeshMeta>,
Res<'w, ExtractedMeshes>,
Res<'w, RenderAssets<Mesh>>,
2021-06-21 23:28:52 +00:00
Query<
'w,
's,
(
&'w ViewUniformOffset,
&'w ViewLights,
&'w MeshViewBindGroups,
),
>,
2021-06-02 02:59:17 +00:00
);
2021-06-21 23:28:52 +00:00
2021-06-02 02:59:17 +00:00
pub struct DrawPbr {
2021-06-21 23:28:52 +00:00
params: SystemState<DrawPbrParams<'static, 'static>>,
2021-06-02 02:59:17 +00:00
}
impl DrawPbr {
pub fn new(world: &mut World) -> Self {
Self {
params: SystemState::new(world),
}
}
}
impl Draw for DrawPbr {
2021-06-21 23:28:52 +00:00
fn draw<'w, 's>(
&'s mut self,
world: &'w World,
pass: &mut TrackedRenderPass<'w>,
2021-06-02 02:59:17 +00:00
view: Entity,
draw_key: usize,
_sort_key: usize,
2021-06-02 02:59:17 +00:00
) {
let (pbr_shaders, mesh_meta, extracted_meshes, meshes, views) = self.params.get(world);
2021-06-21 23:28:52 +00:00
let (view_uniforms, view_lights, mesh_view_bind_groups) = views.get(view).unwrap();
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
let mesh_meta = mesh_meta.into_inner();
2021-06-21 23:28:52 +00:00
pass.set_render_pipeline(&pbr_shaders.into_inner().pipeline);
2021-06-02 02:59:17 +00:00
pass.set_bind_group(
0,
2021-06-21 23:28:52 +00:00
&mesh_view_bind_groups.view,
&[view_uniforms.offset, view_lights.gpu_light_binding_index],
2021-06-02 02:59:17 +00:00
);
pass.set_bind_group(
1,
mesh_meta.mesh_transform_bind_group.as_ref().unwrap(),
2021-06-21 23:28:52 +00:00
&[extracted_mesh.transform_binding_offset],
2021-06-02 02:59:17 +00:00
);
let mesh_draw_info = &mesh_meta.mesh_draw_info[draw_key];
pass.set_bind_group(
2,
&mesh_meta.material_bind_groups[mesh_draw_info.material_bind_group_key],
2021-06-21 23:28:52 +00:00
&[],
);
let gpu_mesh = meshes.into_inner().get(&extracted_mesh.mesh).unwrap();
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
if let Some(index_info) = &gpu_mesh.index_info {
2021-06-21 23:28:52 +00:00
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
2021-06-02 02:59:17 +00:00
pass.draw_indexed(0..index_info.count, 0, 0..1);
} else {
panic!("non-indexed drawing not supported yet")
}
}
}