"base render graph": a common baseline graph

This commit is contained in:
Carter Anderson 2020-05-03 10:54:30 -07:00
parent a4fe37add1
commit 5537eabb8c
11 changed files with 206 additions and 111 deletions

View File

@ -7,4 +7,4 @@ edition = "2018"
[dependencies]
bevy_core = { path = "../bevy_core" }
uuid = { version = "0.8", features = ["v4"] }
uuid = { version = "0.8", features = ["v4", "serde"] }

View File

@ -39,7 +39,7 @@ cgmath = "0.17"
tracing-subscriber = "0.2"
erased-serde = "0.3"
serde = { version = "1", features = ["derive"]}
uuid = { version = "0.8", features = ["v4"] }
uuid = { version = "0.8", features = ["v4", "serde"] }
tracing = "0.1"
itertools = "0.8"
rayon = "1.2"

View File

@ -11,4 +11,4 @@ serde_json = "1.0"
type-uuid = "0.1"
erased-serde = "0.3"
serde = { version = "1", features = ["derive"]}
uuid = { version = "0.8", features = ["v4"] }
uuid = { version = "0.8", features = ["v4", "serde"] }

View File

@ -39,7 +39,7 @@ serde_json = "1.0"
type-uuid = "0.1"
erased-serde = "0.3"
serde = { version = "1", features = ["derive"]}
uuid = { version = "0.8", features = ["v4"] }
uuid = { version = "0.8", features = ["v4", "serde"] }
tracing = "0.1"
itertools = "0.8"
rayon = "1.2"

View File

@ -1,105 +1,62 @@
use crate::{
material::StandardMaterial, nodes::LightsNode, passes::build_main_pass,
pipelines::build_forward_pipeline,
};
use bevy_app::GetEventReader;
use crate::{material::StandardMaterial, nodes::LightsNode, pipelines::build_forward_pipeline};
use bevy_asset::AssetStorage;
use bevy_render::{
base_render_graph,
draw_target::AssignedMeshesDrawTarget,
pipeline::PipelineDescriptor,
render_graph::{
nodes::{
AssetUniformNode, CameraNode, PassNode, UniformNode, WindowSwapChainNode,
WindowTextureNode,
},
nodes::{AssetUniformNode, PassNode, UniformNode},
RenderGraph,
},
shader::Shader,
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
};
use bevy_transform::prelude::LocalToWorld;
use bevy_window::{WindowCreated, WindowReference, WindowResized};
use legion::prelude::Resources;
pub mod node {
pub const LOCAL_TO_WORLD: &str = "local_to_world";
pub const STANDARD_MATERIAL: &str = "standard_material";
pub const LIGHTS: &str = "lights";
}
pub trait ForwardPbrRenderGraphBuilder {
fn add_pbr_graph(&mut self, resources: &Resources) -> &mut Self;
}
impl ForwardPbrRenderGraphBuilder for RenderGraph {
fn add_pbr_graph(&mut self, resources: &Resources) -> &mut Self {
self.add_system_node_named("camera", CameraNode::default(), resources);
self.add_system_node_named(
"local_to_world",
node::LOCAL_TO_WORLD,
UniformNode::<LocalToWorld>::new(true),
resources,
);
self.add_system_node_named(
"standard_material",
node::STANDARD_MATERIAL,
AssetUniformNode::<StandardMaterial>::new(true),
resources,
);
self.add_system_node_named("lights", LightsNode::new(10), resources);
self.add_node_named(
"swapchain",
WindowSwapChainNode::new(
WindowReference::Primary,
resources.get_event_reader::<WindowCreated>(),
resources.get_event_reader::<WindowResized>(),
),
);
self.add_node_named(
"main_pass_depth_texture",
WindowTextureNode::new(
WindowReference::Primary,
TextureDescriptor {
size: Extent3d {
depth: 1,
width: 1,
height: 1,
},
array_layer_count: 1,
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Depth32Float, // PERF: vulkan docs recommend using 24 bit depth for better performance
usage: TextureUsage::OUTPUT_ATTACHMENT,
},
resources.get_event_reader::<WindowCreated>(),
resources.get_event_reader::<WindowResized>(),
),
);
self.add_system_node_named(node::LIGHTS, LightsNode::new(10), resources);
let mut shaders = resources.get_mut::<AssetStorage<Shader>>().unwrap();
let mut pipelines = resources
.get_mut::<AssetStorage<PipelineDescriptor>>()
.unwrap();
let mut main_pass = PassNode::new(build_main_pass());
main_pass.add_pipeline(
pipelines.add_default(build_forward_pipeline(&mut shaders)),
vec![Box::new(AssignedMeshesDrawTarget)],
);
self.add_node_named("main_pass", main_pass);
{
let main_pass: &mut PassNode = self
.get_node_mut(base_render_graph::node::MAIN_PASS)
.unwrap();
main_pass.add_pipeline(
pipelines.add_default(build_forward_pipeline(&mut shaders)),
vec![Box::new(AssignedMeshesDrawTarget)],
);
}
// TODO: replace these with "autowire" groups
self.add_node_edge("camera", "main_pass").unwrap();
self.add_node_edge("standard_material", "main_pass")
self.add_node_edge(node::STANDARD_MATERIAL, base_render_graph::node::MAIN_PASS)
.unwrap();
self.add_node_edge(node::LOCAL_TO_WORLD, base_render_graph::node::MAIN_PASS)
.unwrap();
self.add_node_edge(node::LIGHTS, base_render_graph::node::MAIN_PASS)
.unwrap();
self.add_node_edge("local_to_world", "main_pass").unwrap();
self.add_node_edge("lights", "main_pass").unwrap();
self.add_slot_edge(
"swapchain",
WindowSwapChainNode::OUT_TEXTURE,
"main_pass",
"color",
)
.unwrap();
self.add_slot_edge(
"main_pass_depth_texture",
WindowTextureNode::OUT_TEXTURE,
"main_pass",
"depth",
)
.unwrap();
self
}
}

View File

@ -2,7 +2,6 @@ pub mod entity;
pub mod light;
pub mod material;
pub mod nodes;
pub mod passes;
pub mod pipelines;
mod forward_pbr_render_graph;
@ -14,10 +13,10 @@ use bevy_render::{render_graph::RenderGraph, shader};
use legion::prelude::IntoSystem;
use material::StandardMaterial;
/// NOTE: this isn't PBR yet. consider this name "aspirational" :)
#[derive(Default)]
pub struct PbrPlugin;
// NOTE: this isn't PBR yet. consider this name "aspirational" :)
impl AppPlugin for PbrPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_resource(AssetStorage::<StandardMaterial>::new())

View File

@ -1,28 +0,0 @@
use bevy_render::pass::{
LoadOp, PassDescriptor, RenderPassColorAttachmentDescriptor,
RenderPassDepthStencilAttachmentDescriptor, StoreOp, TextureAttachment,
};
use bevy_render::Color;
pub fn build_main_pass() -> PassDescriptor {
PassDescriptor {
color_attachments: vec![RenderPassColorAttachmentDescriptor {
attachment: TextureAttachment::Input("color".to_string()),
resolve_target: None,
load_op: LoadOp::Clear,
store_op: StoreOp::Store,
clear_color: Color::rgb(0.1, 0.1, 0.1),
}],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachmentDescriptor {
attachment: TextureAttachment::Input("depth".to_string()),
depth_load_op: LoadOp::Clear,
depth_store_op: StoreOp::Store,
stencil_load_op: LoadOp::Clear,
stencil_store_op: StoreOp::Store,
clear_depth: 1.0,
clear_stencil: 0,
}),
sample_count: 1,
}
}

View File

@ -1,3 +0,0 @@
mod main;
pub use main::*;

View File

@ -0,0 +1,155 @@
use crate::{
pass::{
LoadOp, PassDescriptor, RenderPassColorAttachmentDescriptor,
RenderPassDepthStencilAttachmentDescriptor, StoreOp, TextureAttachment,
},
render_graph::{
nodes::{
Camera2dNode, CameraNode, PassNode, WindowSwapChainNode,
WindowTextureNode,
},
RenderGraph,
},
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
Color,
};
use bevy_app::GetEventReader;
use bevy_window::{WindowCreated, WindowReference, WindowResized};
use legion::prelude::Resources;
pub struct BaseRenderGraphConfig {
add_2d_camera: bool,
add_3d_camera: bool,
add_main_pass: bool,
connect_main_pass_to_swapchain: bool,
}
pub mod node {
pub const PRIMARY_SWAP_CHAIN: &str = "swapchain";
pub const CAMERA: &str = "camera";
pub const CAMERA2D: &str = "camera2d";
pub const MAIN_PASS_DEPTH_TEXTURE: &str = "main_pass_depth_texture";
pub const MAIN_PASS: &str = "main_pass";
}
impl Default for BaseRenderGraphConfig {
fn default() -> Self {
BaseRenderGraphConfig {
add_2d_camera: true,
add_3d_camera: true,
add_main_pass: true,
connect_main_pass_to_swapchain: true,
}
}
}
/// The "base render graph" provides a core set of render graph nodes which can be used to build any graph.
/// By itself this graph doesn't do much, but it allows Render plugins to interop with each other by having a common
/// set of nodes. It can be customized using `BaseRenderGraphConfig`.
pub trait BaseRenderGraphBuilder {
fn add_base_graph(
&mut self,
resources: &Resources,
config: &BaseRenderGraphConfig,
) -> &mut Self;
}
impl BaseRenderGraphBuilder for RenderGraph {
fn add_base_graph(
&mut self,
resources: &Resources,
config: &BaseRenderGraphConfig,
) -> &mut Self {
if config.add_3d_camera {
self.add_system_node_named(node::CAMERA, CameraNode::default(), resources);
}
if config.add_2d_camera {
self.add_system_node_named(node::CAMERA2D, Camera2dNode::default(), resources);
}
if config.add_main_pass {
self.add_node_named(
node::MAIN_PASS_DEPTH_TEXTURE,
WindowTextureNode::new(
WindowReference::Primary,
TextureDescriptor {
size: Extent3d {
depth: 1,
width: 1,
height: 1,
},
array_layer_count: 1,
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Depth32Float, // PERF: vulkan docs recommend using 24 bit depth for better performance
usage: TextureUsage::OUTPUT_ATTACHMENT,
},
resources.get_event_reader::<WindowCreated>(),
resources.get_event_reader::<WindowResized>(),
),
);
self.add_node_named(
node::MAIN_PASS,
PassNode::new(PassDescriptor {
color_attachments: vec![RenderPassColorAttachmentDescriptor {
attachment: TextureAttachment::Input("color".to_string()),
resolve_target: None,
load_op: LoadOp::Clear,
store_op: StoreOp::Store,
clear_color: Color::rgb(0.1, 0.1, 0.1),
}],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachmentDescriptor {
attachment: TextureAttachment::Input("depth".to_string()),
depth_load_op: LoadOp::Clear,
depth_store_op: StoreOp::Store,
stencil_load_op: LoadOp::Clear,
stencil_store_op: StoreOp::Store,
clear_depth: 1.0,
clear_stencil: 0,
}),
sample_count: 1,
}),
);
self.add_slot_edge(
node::MAIN_PASS_DEPTH_TEXTURE,
WindowTextureNode::OUT_TEXTURE,
node::MAIN_PASS,
"depth",
)
.unwrap();
if config.add_3d_camera {
self.add_node_edge(node::CAMERA, node::MAIN_PASS).unwrap();
}
if config.add_2d_camera {
self.add_node_edge(node::CAMERA2D, node::MAIN_PASS).unwrap();
}
}
self.add_node_named(
node::PRIMARY_SWAP_CHAIN,
WindowSwapChainNode::new(
WindowReference::Primary,
resources.get_event_reader::<WindowCreated>(),
resources.get_event_reader::<WindowResized>(),
),
);
if config.connect_main_pass_to_swapchain {
self.add_slot_edge(
node::PRIMARY_SWAP_CHAIN,
WindowSwapChainNode::OUT_TEXTURE,
node::MAIN_PASS,
"color",
)
.unwrap();
}
self
}
}

View File

@ -16,6 +16,7 @@ pub use renderable::*;
pub use vertex::Vertex;
pub mod base_render_graph;
pub mod draw_target;
pub mod pass;
pub mod pipeline;
@ -38,6 +39,7 @@ use self::{
texture::Texture,
};
use base_render_graph::{BaseRenderGraphBuilder, BaseRenderGraphConfig};
use bevy_app::{stage, AppBuilder, AppPlugin};
use bevy_asset::AssetStorage;
use mesh::mesh_resource_provider_system;
@ -46,17 +48,30 @@ use render_graph::RenderGraph;
pub static RENDER_RESOURCE_STAGE: &str = "render_resource";
pub static RENDER_STAGE: &str = "render";
#[derive(Default)]
pub struct RenderPlugin;
pub struct RenderPlugin {
/// configures the "base render graph". If this is not `None`, the "base render graph" will be added
pub base_render_graph_config: Option<BaseRenderGraphConfig>,
}
impl RenderPlugin {}
impl Default for RenderPlugin {
fn default() -> Self {
RenderPlugin {
base_render_graph_config: Some(BaseRenderGraphConfig::default()),
}
}
}
impl AppPlugin for RenderPlugin {
fn build(&self, app: &mut AppBuilder) {
let mut render_graph = RenderGraph::default();
if let Some(ref config) = self.base_render_graph_config {
render_graph.add_base_graph(app.resources(), config);
}
app.add_stage_after(stage::POST_UPDATE, RENDER_RESOURCE_STAGE)
.add_stage_after(RENDER_RESOURCE_STAGE, RENDER_STAGE)
// resources
.add_resource(RenderGraph::default())
.add_resource(render_graph)
.add_resource(AssetStorage::<Mesh>::new())
.add_resource(AssetStorage::<Texture>::new())
.add_resource(AssetStorage::<Shader>::new())

View File

@ -9,4 +9,4 @@ legion = { path = "../bevy_legion", features = ["serialize"] }
serde = { version = "1", features = ["derive"]}
erased-serde = "0.3"
type-uuid = "0.1"
uuid = { version = "0.8", features = ["v4"] }
uuid = { version = "0.8", features = ["v4", "serde"] }