RenderGraph2: Finish graph executor, fix window textures

This commit is contained in:
Carter Anderson 2020-04-23 20:53:38 -07:00
parent 8326a1a3c2
commit 5780bf4025
11 changed files with 226 additions and 120 deletions

View file

@ -50,14 +50,15 @@ use self::{
use bevy_app::{stage, AppBuilder, AppPlugin, GetEventReader};
use bevy_asset::AssetStorage;
use bevy_transform::prelude::LocalToWorld;
use bevy_window::{WindowCreated, WindowResized};
use bevy_window::{WindowCreated, WindowReference, WindowResized};
use pass::PassDescriptor;
use pipeline::pipelines::build_forward_pipeline;
use render_graph_2::{
nodes::{Camera2dNode, CameraNode, PassNode, SwapChainWindowSource, WindowSwapChainNode},
nodes::{Camera2dNode, CameraNode, PassNode, WindowSwapChainNode, WindowTextureNode},
RenderGraph2,
};
use render_resource::resource_providers::mesh_resource_provider_system;
use texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage};
pub static RENDER_RESOURCE_STAGE: &str = "render_resource";
pub static RENDER_STAGE: &str = "render";
@ -126,7 +127,28 @@ impl AppPlugin for RenderPlugin {
render_graph.add_node_named(
"swapchain",
WindowSwapChainNode::new(
SwapChainWindowSource::Primary,
WindowReference::Primary,
resources.get_event_reader::<WindowCreated>(),
resources.get_event_reader::<WindowResized>(),
),
);
render_graph.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 recommends using 24 bit depth for better performance
usage: TextureUsage::OUTPUT_ATTACHMENT,
},
resources.get_event_reader::<WindowCreated>(),
resources.get_event_reader::<WindowResized>(),
),
@ -164,7 +186,20 @@ impl AppPlugin for RenderPlugin {
render_graph.add_node_edge("camera", "main_pass").unwrap();
render_graph.add_node_edge("camera2d", "main_pass").unwrap();
render_graph
.add_slot_edge("swapchain", WindowSwapChainNode::OUT_TEXTURE, "main_pass", "color")
.add_slot_edge(
"swapchain",
WindowSwapChainNode::OUT_TEXTURE,
"main_pass",
"color",
)
.unwrap();
render_graph
.add_slot_edge(
"main_pass_depth_texture",
WindowTextureNode::OUT_TEXTURE,
"main_pass",
"depth",
)
.unwrap();
}
app.add_resource(render_graph);

View file

@ -131,10 +131,10 @@ impl RenderGraph2 {
{
let output_node = self.get_node_state_mut(output_node_id)?;
output_node.add_output_edge(edge.clone())?;
output_node.edges.add_output_edge(edge.clone())?;
}
let input_node = self.get_node_state_mut(input_node_id)?;
input_node.add_input_edge(edge)?;
input_node.edges.add_input_edge(edge)?;
Ok(())
}
@ -156,10 +156,10 @@ impl RenderGraph2 {
{
let output_node = self.get_node_state_mut(output_node_id)?;
output_node.add_output_edge(edge.clone())?;
output_node.edges.add_output_edge(edge.clone())?;
}
let input_node = self.get_node_state_mut(input_node_id)?;
input_node.add_input_edge(edge)?;
input_node.edges.add_input_edge(edge)?;
Ok(())
}
@ -185,7 +185,7 @@ impl RenderGraph2 {
if let Some(Edge::SlotEdge {
output_node: current_output_node,
..
}) = input_node_state.input_edges.iter().find(|e| {
}) = input_node_state.edges.input_edges.iter().find(|e| {
if let Edge::SlotEdge {
input_index: current_input_index,
..
@ -222,9 +222,9 @@ impl RenderGraph2 {
let output_node_state = self.get_node_state(edge.get_output_node());
let input_node_state = self.get_node_state(edge.get_input_node());
if let Ok(output_node_state) = output_node_state {
if output_node_state.output_edges.contains(edge) {
if output_node_state.edges.output_edges.contains(edge) {
if let Ok(input_node_state) = input_node_state {
if input_node_state.input_edges.contains(edge) {
if input_node_state.edges.input_edges.contains(edge) {
return true;
}
}
@ -270,6 +270,7 @@ impl RenderGraph2 {
) -> Result<impl Iterator<Item = (&Edge, &NodeState)>, RenderGraphError> {
let node = self.get_node_state(label)?;
Ok(node
.edges
.input_edges
.iter()
.map(|edge| (edge, edge.get_output_node()))
@ -284,6 +285,7 @@ impl RenderGraph2 {
) -> Result<impl Iterator<Item = (&Edge, &NodeState)>, RenderGraphError> {
let node = self.get_node_state(label)?;
Ok(node
.edges
.output_edges
.iter()
.map(|edge| (edge, edge.get_input_node()))

View file

@ -39,56 +39,13 @@ pub trait SystemNode: Node {
fn get_system(&self, resources: &mut Resources) -> Box<dyn Schedulable>;
}
pub struct NodeState {
pub struct Edges {
pub id: NodeId,
pub name: Option<Cow<'static, str>>,
pub node: Box<dyn Node>,
pub input_slots: ResourceSlots,
pub output_slots: ResourceSlots,
pub input_edges: Vec<Edge>,
pub output_edges: Vec<Edge>,
}
impl Debug for NodeState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{:?} ({:?})", self.id, self.name)
}
}
impl NodeState {
pub fn new<T>(id: NodeId, node: T) -> Self
where
T: Node,
{
NodeState {
id,
name: None,
input_slots: ResourceSlots::from(node.input()),
output_slots: ResourceSlots::from(node.output()),
node: Box::new(node),
input_edges: Vec::new(),
output_edges: Vec::new(),
}
}
pub fn node<T>(&self) -> Result<&T, RenderGraphError>
where
T: Node,
{
self.node
.downcast_ref::<T>()
.ok_or_else(|| RenderGraphError::WrongNodeType)
}
pub fn node_mut<T>(&mut self) -> Result<&mut T, RenderGraphError>
where
T: Node,
{
self.node
.downcast_mut::<T>()
.ok_or_else(|| RenderGraphError::WrongNodeType)
}
impl Edges {
pub(crate) fn add_input_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
if self.has_input_edge(&edge) {
return Err(RenderGraphError::EdgeAlreadyExists(edge.clone()));
@ -144,10 +101,64 @@ impl NodeState {
node: self.id,
})
}
}
pub struct NodeState {
pub id: NodeId,
pub name: Option<Cow<'static, str>>,
pub node: Box<dyn Node>,
pub input_slots: ResourceSlots,
pub output_slots: ResourceSlots,
pub edges: Edges,
}
impl Debug for NodeState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{:?} ({:?})", self.id, self.name)
}
}
impl NodeState {
pub fn new<T>(id: NodeId, node: T) -> Self
where
T: Node,
{
NodeState {
id,
name: None,
input_slots: ResourceSlots::from(node.input()),
output_slots: ResourceSlots::from(node.output()),
node: Box::new(node),
edges: Edges {
id,
input_edges: Vec::new(),
output_edges: Vec::new(),
},
}
}
pub fn node<T>(&self) -> Result<&T, RenderGraphError>
where
T: Node,
{
self.node
.downcast_ref::<T>()
.ok_or_else(|| RenderGraphError::WrongNodeType)
}
pub fn node_mut<T>(&mut self) -> Result<&mut T, RenderGraphError>
where
T: Node,
{
self.node
.downcast_mut::<T>()
.ok_or_else(|| RenderGraphError::WrongNodeType)
}
pub fn validate_output_slots(&self) -> Result<(), RenderGraphError> {
for i in 0..self.output_slots.len() {
self.get_output_slot_edge(i)?;
self.edges.get_output_slot_edge(i)?;
}
Ok(())
@ -155,7 +166,7 @@ impl NodeState {
pub fn validate_input_slots(&self) -> Result<(), RenderGraphError> {
for i in 0..self.input_slots.len() {
self.get_input_slot_edge(i)?;
self.edges.get_input_slot_edge(i)?;
}
Ok(())

View file

@ -2,13 +2,13 @@ use super::RenderGraphError;
use crate::render_resource::{RenderResource, ResourceInfo};
use std::borrow::Cow;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ResourceSlot {
pub resource: Option<RenderResource>,
pub info: ResourceSlotInfo,
}
#[derive(Default, Debug)]
#[derive(Default, Debug, Clone)]
pub struct ResourceSlots {
slots: Vec<ResourceSlot>,
}
@ -93,6 +93,10 @@ impl ResourceSlots {
self.slots.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut ResourceSlot> {
self.slots.iter_mut()
}
pub fn len(&self) -> usize {
self.slots.len()
}

View file

@ -23,7 +23,7 @@ impl PassNode {
for color_attachment in descriptor.color_attachments.iter() {
if let TextureAttachment::Input(ref name) = color_attachment.attachment {
inputs.push(ResourceSlotInfo::new(name.to_string(), ResourceInfo::Texture));
color_attachment_input_indices.push(Some(inputs.len()));
color_attachment_input_indices.push(Some(inputs.len() - 1));
} else {
color_attachment_input_indices.push(None);
}
@ -33,7 +33,7 @@ impl PassNode {
if let Some(ref depth_stencil_attachment)= descriptor.depth_stencil_attachment {
if let TextureAttachment::Input(ref name) = depth_stencil_attachment.attachment {
inputs.push(ResourceSlotInfo::new(name.to_string(), ResourceInfo::Texture));
depth_stencil_attachment_input_index = Some(inputs.len());
depth_stencil_attachment_input_index = Some(inputs.len() - 1);
}
}

View file

@ -4,23 +4,12 @@ use crate::{
renderer_2::RenderContext,
};
use bevy_app::{EventReader, Events};
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
use bevy_window::{WindowCreated, WindowResized, Windows, WindowReference};
use legion::prelude::*;
use std::borrow::Cow;
pub enum SwapChainWindowSource {
Primary,
Id(WindowId),
}
impl Default for SwapChainWindowSource {
fn default() -> Self {
SwapChainWindowSource::Primary
}
}
pub struct WindowSwapChainNode {
source_window: SwapChainWindowSource,
window_reference: WindowReference,
window_created_event_reader: EventReader<WindowCreated>,
window_resized_event_reader: EventReader<WindowResized>,
}
@ -28,12 +17,12 @@ pub struct WindowSwapChainNode {
impl WindowSwapChainNode {
pub const OUT_TEXTURE: &'static str = "texture";
pub fn new(
source_window: SwapChainWindowSource,
window_reference: WindowReference,
window_created_event_reader: EventReader<WindowCreated>,
window_resized_event_reader: EventReader<WindowResized>,
) -> Self {
WindowSwapChainNode {
source_window,
window_reference,
window_created_event_reader,
window_resized_event_reader,
}
@ -62,26 +51,22 @@ impl Node for WindowSwapChainNode {
let window_resized_events = resources.get::<Events<WindowResized>>().unwrap();
let windows = resources.get::<Windows>().unwrap();
let render_resources = render_context.resources_mut();
let window = match self.source_window {
SwapChainWindowSource::Primary => {
let window = match self.window_reference {
WindowReference::Primary => {
windows.get_primary().expect("No primary window exists")
}
SwapChainWindowSource::Id(id) => windows
WindowReference::Id(id) => windows
.get(id)
.expect("Received window resized event for non-existent window"),
};
// create window swapchain
if let Some(_) = window_created_events
.find_latest(&mut self.window_created_event_reader, |e| e.id == window.id)
{
render_resources.create_swap_chain(window);
}
let render_resources = render_context.resources_mut();
// resize window swapchain
if let Some(_) = window_resized_events
.find_latest(&mut self.window_resized_event_reader, |e| e.id == window.id)
// create window swapchain when window is resized or created
if window_created_events
.find_latest(&mut self.window_created_event_reader, |e| e.id == window.id).is_some() ||
window_resized_events
.find_latest(&mut self.window_resized_event_reader, |e| e.id == window.id).is_some()
{
render_resources.create_swap_chain(window);
}

View file

@ -1,23 +1,42 @@
use crate::{
render_graph_2::{Node, ResourceSlots, ResourceSlotInfo},
render_graph_2::{Node, ResourceSlotInfo, ResourceSlots},
render_resource::ResourceInfo,
renderer_2::RenderContext,
texture::TextureDescriptor,
};
use bevy_app::{EventReader, Events};
use bevy_window::WindowResized;
use bevy_window::{WindowCreated, WindowReference, WindowResized, Windows};
use legion::prelude::*;
use std::borrow::Cow;
pub struct WindowTextureNode {
pub descriptor: TextureDescriptor,
window_reference: WindowReference,
descriptor: TextureDescriptor,
window_created_event_reader: EventReader<WindowCreated>,
window_resized_event_reader: EventReader<WindowResized>,
}
impl WindowTextureNode {
pub const OUT_TEXTURE: &'static str = "texture";
pub fn new(
window_reference: WindowReference,
descriptor: TextureDescriptor,
window_created_event_reader: EventReader<WindowCreated>,
window_resized_event_reader: EventReader<WindowResized>,
) -> Self {
WindowTextureNode {
window_reference,
descriptor,
window_created_event_reader,
window_resized_event_reader,
}
}
}
impl Node for WindowTextureNode {
fn output(&self) -> &[ResourceSlotInfo] {
static OUTPUT: &[ResourceSlotInfo] = &[ResourceSlotInfo {
name: Cow::Borrowed("texture"),
name: Cow::Borrowed(WindowTextureNode::OUT_TEXTURE),
resource_type: ResourceInfo::Texture,
}];
OUTPUT
@ -32,15 +51,29 @@ impl Node for WindowTextureNode {
output: &mut ResourceSlots,
) {
const WINDOW_TEXTURE: usize = 0;
let window_created_events = resources.get::<Events<WindowCreated>>().unwrap();
let window_resized_events = resources.get::<Events<WindowResized>>().unwrap();
if let Some(event) = window_resized_events.latest(&mut self.window_resized_event_reader) {
let windows = resources.get::<Windows>().unwrap();
let window = match self.window_reference {
WindowReference::Primary => windows.get_primary().expect("No primary window exists"),
WindowReference::Id(id) => windows
.get(id)
.expect("Received window resized event for non-existent window"),
};
if window_created_events
.find_latest(&mut self.window_created_event_reader, |e| e.id == window.id).is_some() ||
window_resized_events
.find_latest(&mut self.window_resized_event_reader, |e| e.id == window.id).is_some()
{
let render_resources = render_context.resources_mut();
if let Some(old_texture) = output.get(WINDOW_TEXTURE) {
render_resources.remove_texture(old_texture);
}
self.descriptor.size.width = event.width;
self.descriptor.size.height = event.height;
self.descriptor.size.width = window.width;
self.descriptor.size.height = window.height;
let texture_resource = render_resources.create_texture(&self.descriptor);
output.set(WINDOW_TEXTURE, texture_resource);
}

View file

@ -187,6 +187,7 @@ fn stage_node(
// don't re-visit nodes or visit them before all of their parents have been visited
if node_stages_and_jobs.contains_key(&node.id)
|| node
.edges
.input_edges
.iter()
.find(|e| !node_stages_and_jobs.contains_key(&e.get_output_node()))
@ -204,6 +205,7 @@ fn stage_node(
// check to see if the current node has a parent. if so, grab the parent with the highest stage
if let Some((max_parent_stage, max_parent_job)) = node
.edges
.input_edges
.iter()
.map(|e| {
@ -215,6 +217,7 @@ fn stage_node(
{
// count the number of parents that are in the highest stage
let max_stage_parent_count = node
.edges
.input_edges
.iter()
.filter(|e| {

View file

@ -11,7 +11,7 @@ use bevy_render::{
},
pipeline::{BindGroupDescriptor, BindType, PipelineDescriptor},
render_resource::{
resource_name, RenderResource, RenderResourceAssignments, RenderResourceSetId, ResourceInfo,
RenderResource, RenderResourceAssignments, RenderResourceSetId, ResourceInfo,
},
renderer_2::{RenderContext, RenderResourceContext},
shader::Shader,
@ -475,26 +475,13 @@ fn get_texture_view<'a>(
attachment: &TextureAttachment,
) -> &'a wgpu::TextureView {
match attachment {
TextureAttachment::Name(name) => match name.as_str() {
resource_name::texture::SWAP_CHAIN => {
if let Some(primary_swap_chain) = refs.swap_chain_outputs.values().next() {
&primary_swap_chain.view
} else {
panic!("No primary swap chain found for color attachment");
}
}
_ => match global_render_resource_assignments.get(&name) {
TextureAttachment::Name(name) => match global_render_resource_assignments.get(&name) {
Some(resource) => refs.textures.get(&resource).unwrap(),
None => {
// if let Some(swap_chain_output) = swap_chain_outputs.get(name) {
// &swap_chain_output.view
// } else {
panic!("Color attachment {} does not exist", name);
// }
}
},
},
TextureAttachment::RenderResource(render_resource) => refs.textures.get(&render_resource).unwrap(),
TextureAttachment::RenderResource(render_resource) => refs.textures.get(&render_resource).unwrap_or_else(|| &refs.swap_chain_outputs.get(&render_resource).unwrap().view),
TextureAttachment::Input(_) => panic!("Encountered unset TextureAttachment::Input. The RenderGraph executor should always set TextureAttachment::Inputs to TextureAttachment::RenderResource before running. This is a bug"),
}
}

View file

@ -1,7 +1,10 @@
use super::{WgpuRenderContext, WgpuRenderResourceContext};
use bevy_render::{render_graph_2::StageBorrow, renderer_2::GlobalRenderResourceContext};
use bevy_render::{
render_graph_2::{Edge, NodeId, ResourceSlots, StageBorrow},
renderer_2::GlobalRenderResourceContext,
};
use legion::prelude::{Resources, World};
use std::sync::Arc;
use std::{collections::HashMap, sync::Arc};
pub struct WgpuRenderGraphExecutor {
pub max_thread_count: usize,
@ -21,6 +24,7 @@ impl WgpuRenderGraphExecutor {
.context
.downcast_mut::<WgpuRenderResourceContext>()
.unwrap();
let mut node_outputs: HashMap<NodeId, ResourceSlots> = HashMap::new();
for stage in stages.iter_mut() {
// TODO: sort jobs and slice by "amount of work" / weights
// stage.jobs.sort_by_key(|j| j.node_states.len());
@ -29,6 +33,7 @@ impl WgpuRenderGraphExecutor {
let chunk_size = (stage.jobs.len() + self.max_thread_count - 1) / self.max_thread_count; // divide ints rounding remainder up
let mut actual_thread_count = 0;
crossbeam_utils::thread::scope(|s| {
let node_outputs = &node_outputs;
for jobs_chunk in stage.jobs.chunks_mut(chunk_size) {
let sender = sender.clone();
let world = &*world;
@ -38,8 +43,37 @@ impl WgpuRenderGraphExecutor {
s.spawn(move |_| {
let mut render_context =
WgpuRenderContext::new(device, render_resource_context);
let mut local_node_outputs = HashMap::new();
for job in jobs_chunk.iter_mut() {
for node_state in job.node_states.iter_mut() {
// bind inputs from connected node outputs
for (i, mut input_slot) in node_state.input_slots.iter_mut().enumerate()
{
if let Edge::SlotEdge {
output_node,
output_index,
..
} = node_state.edges.get_input_slot_edge(i).unwrap()
{
let outputs =
if let Some(outputs) = node_outputs.get(output_node) {
outputs
} else if let Some(outputs) =
local_node_outputs.get(output_node)
{
outputs
} else {
panic!("node inputs not set")
};
let output_resource = outputs
.get(*output_index)
.expect("output should be set");
input_slot.resource = Some(output_resource);
} else {
panic!("no edge connected to input")
}
}
node_state.node.update(
world,
resources,
@ -47,9 +81,14 @@ impl WgpuRenderGraphExecutor {
&node_state.input_slots,
&mut node_state.output_slots,
);
local_node_outputs
.insert(node_state.id, node_state.output_slots.clone());
}
}
sender.send(render_context.finish()).unwrap();
sender
.send((render_context.finish(), local_node_outputs))
.unwrap();
});
}
})
@ -57,10 +96,12 @@ impl WgpuRenderGraphExecutor {
let mut command_buffers = Vec::new();
for _i in 0..actual_thread_count {
let command_buffer = receiver.recv().unwrap();
let (command_buffer, mut local_node_outputs) = receiver.recv().unwrap();
if let Some(command_buffer) = command_buffer {
command_buffers.push(command_buffer);
}
node_outputs.extend(local_node_outputs.drain());
}
queue.submit(&command_buffers);

View file

@ -1,5 +1,10 @@
use uuid::Uuid;
pub enum WindowReference {
Primary,
Id(WindowId),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct WindowId(Uuid);