Last active
August 28, 2024 15:09
-
-
Save matthewjberger/4ddb93f9ce7b14dde7678610858ef118 to your computer and use it in GitHub Desktop.
Framegraphs in rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "framegraph-system" | |
version = "0.1.0" | |
edition = "2021" | |
[dependencies] | |
petgraph = "0.6.3" | |
wgpu = "0.15" | |
pollster = "0.3" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::collections::HashMap; | |
use petgraph::graph::{DiGraph, NodeIndex}; | |
pub trait Resource: std::fmt::Debug { | |
fn name(&self) -> &str; | |
} | |
pub trait RenderPass { | |
fn name(&self) -> &str; | |
fn inputs(&self) -> &[String]; | |
fn outputs(&self) -> &[String]; | |
fn execute(&self, context: &mut dyn ExecutionContext, resources: &HashMap<String, Box<dyn Resource>>); | |
} | |
pub trait ExecutionContext { | |
fn create_resource(&mut self, name: &str, resource: Box<dyn Resource>); | |
fn get_resource(&self, name: &str) -> Option<&dyn Resource>; | |
} | |
pub struct Framegraph { | |
graph: DiGraph<Box<dyn RenderPass>, ()>, | |
resources: HashMap<String, Box<dyn Resource>>, | |
} | |
impl Framegraph { | |
pub fn new() -> Self { | |
Framegraph { | |
graph: DiGraph::new(), | |
resources: HashMap::new(), | |
} | |
} | |
pub fn add_pass(&mut self, pass: Box<dyn RenderPass>) -> NodeIndex { | |
self.graph.add_node(pass) | |
} | |
pub fn add_dependency(&mut self, from: NodeIndex, to: NodeIndex) { | |
self.graph.add_edge(from, to, ()); | |
} | |
pub fn add_resource(&mut self, resource: Box<dyn Resource>) { | |
self.resources.insert(resource.name().to_string(), resource); | |
} | |
pub fn execute(&self, context: &mut dyn ExecutionContext) { | |
let mut visited = vec![false; self.graph.node_count()]; | |
for node in self.graph.node_indices() { | |
self.dfs_execute(node, context, &mut visited); | |
} | |
} | |
fn dfs_execute(&self, node: NodeIndex, context: &mut dyn ExecutionContext, visited: &mut Vec<bool>) { | |
if visited[node.index()] { | |
return; | |
} | |
for dep in self.graph.neighbors_directed(node, petgraph::Direction::Incoming) { | |
self.dfs_execute(dep, context, visited); | |
} | |
let pass = &self.graph[node]; | |
pass.execute(context, &self.resources); | |
visited[node.index()] = true; | |
} | |
} | |
pub fn visualize_framegraph(fg: &Framegraph) -> String { | |
use petgraph::dot::{Dot, Config}; | |
format!("{:?}", Dot::with_config(&fg.graph, &[Config::EdgeNoLabel])) | |
} | |
# src/wgpu_impl.rs | |
use crate::{Resource, RenderPass, ExecutionContext, Framegraph}; | |
use std::collections::HashMap; | |
use std::sync::Arc; | |
use wgpu; | |
#[derive(Debug)] | |
pub enum WgpuResource { | |
Texture(Arc<wgpu::Texture>), | |
Buffer(Arc<wgpu::Buffer>), | |
} | |
impl Resource for WgpuResource { | |
fn name(&self) -> &str { | |
match self { | |
WgpuResource::Texture(_) => "Texture", | |
WgpuResource::Buffer(_) => "Buffer", | |
} | |
} | |
} | |
pub struct WgpuExecutionContext<'a> { | |
pub device: &'a wgpu::Device, | |
pub queue: &'a wgpu::Queue, | |
pub encoder: &'a mut wgpu::CommandEncoder, | |
} | |
impl<'a> ExecutionContext for WgpuExecutionContext<'a> { | |
fn create_resource(&mut self, name: &str, resource: Box<dyn Resource>) { | |
// Implementation would create WGPU-specific resources | |
println!("Creating resource: {}", name); | |
} | |
fn get_resource(&self, name: &str) -> Option<&dyn Resource> { | |
// Implementation would retrieve WGPU-specific resources | |
println!("Getting resource: {}", name); | |
None | |
} | |
} | |
pub struct ShadowMappingPass; | |
pub struct ZPrepass; | |
pub struct LightCullingPass; | |
pub struct ForwardRenderingPass; | |
pub struct TransparencyPass; | |
pub struct PostProcessingPass; | |
impl RenderPass for ShadowMappingPass { | |
fn name(&self) -> &str { "Shadow Mapping" } | |
fn inputs(&self) -> &[String] { &[] } | |
fn outputs(&self) -> &[String] { &["shadow_map".to_string()] } | |
fn execute(&self, context: &mut dyn ExecutionContext, resources: &HashMap<String, Box<dyn Resource>>) { | |
println!("Executing Shadow Mapping pass"); | |
// Actual implementation would use WGPU to render shadow maps | |
} | |
} | |
impl RenderPass for ZPrepass { | |
fn name(&self) -> &str { "Z-Prepass" } | |
fn inputs(&self) -> &[String] { &[] } | |
fn outputs(&self) -> &[String] { &["depth".to_string()] } | |
fn execute(&self, context: &mut dyn ExecutionContext, resources: &HashMap<String, Box<dyn Resource>>) { | |
println!("Executing Z-Prepass"); | |
// Actual implementation would use WGPU to render depth | |
} | |
} | |
impl RenderPass for LightCullingPass { | |
fn name(&self) -> &str { "Light Culling" } | |
fn inputs(&self) -> &[String] { &["depth".to_string()] } | |
fn outputs(&self) -> &[String] { &["light_list".to_string(), "light_grid".to_string()] } | |
fn execute(&self, context: &mut dyn ExecutionContext, resources: &HashMap<String, Box<dyn Resource>>) { | |
println!("Executing Light Culling pass"); | |
// Actual implementation would use WGPU to perform light culling | |
} | |
} | |
impl RenderPass for ForwardRenderingPass { | |
fn name(&self) -> &str { "Forward Rendering" } | |
fn inputs(&self) -> &[String] { &["depth".to_string(), "shadow_map".to_string(), "light_list".to_string(), "light_grid".to_string()] } | |
fn outputs(&self) -> &[String] { &["color".to_string()] } | |
fn execute(&self, context: &mut dyn ExecutionContext, resources: &HashMap<String, Box<dyn Resource>>) { | |
println!("Executing Forward Rendering pass"); | |
// Actual implementation would use WGPU to perform forward rendering | |
} | |
} | |
impl RenderPass for TransparencyPass { | |
fn name(&self) -> &str { "Transparency" } | |
fn inputs(&self) -> &[String] { &["depth".to_string(), "color".to_string(), "shadow_map".to_string(), "light_list".to_string(), "light_grid".to_string()] } | |
fn outputs(&self) -> &[String] { &["final_color".to_string()] } | |
fn execute(&self, context: &mut dyn ExecutionContext, resources: &HashMap<String, Box<dyn Resource>>) { | |
println!("Executing Transparency pass"); | |
// Actual implementation would use WGPU to render transparent objects | |
} | |
} | |
impl RenderPass for PostProcessingPass { | |
fn name(&self) -> &str { "Post-processing" } | |
fn inputs(&self) -> &[String] { &["final_color".to_string()] } | |
fn outputs(&self) -> &[String] { &["output".to_string()] } | |
fn execute(&self, context: &mut dyn ExecutionContext, resources: &HashMap<String, Box<dyn Resource>>) { | |
println!("Executing Post-processing pass"); | |
// Actual implementation would use WGPU to apply post-processing effects | |
} | |
} | |
pub fn create_advanced_forward_plus_framegraph() -> Framegraph { | |
let mut fg = Framegraph::new(); | |
// Add resources | |
fg.add_resource(Box::new(WgpuResource::Texture(Arc::new(wgpu::Texture::default())))); | |
fg.add_resource(Box::new(WgpuResource::Buffer(Arc::new(wgpu::Buffer::default())))); | |
// Add more resources... | |
// Add passes | |
let shadow_node = fg.add_pass(Box::new(ShadowMappingPass)); | |
let z_prepass_node = fg.add_pass(Box::new(ZPrepass)); | |
let light_culling_node = fg.add_pass(Box::new(LightCullingPass)); | |
let forward_node = fg.add_pass(Box::new(ForwardRenderingPass)); | |
let transparency_node = fg.add_pass(Box::new(TransparencyPass)); | |
let post_processing_node = fg.add_pass(Box::new(PostProcessingPass)); | |
// Add dependencies | |
fg.add_dependency(shadow_node, forward_node); | |
fg.add_dependency(z_prepass_node, light_culling_node); | |
fg.add_dependency(light_culling_node, forward_node); | |
fg.add_dependency(forward_node, transparency_node); | |
fg.add_dependency(transparency_node, post_processing_node); | |
fg | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use crate::wgpu_impl::{create_advanced_forward_plus_framegraph, WgpuExecutionContext}; | |
use framegraph_system::visualize_framegraph; | |
fn main() { | |
// Setup wgpu | |
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default()); | |
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default())).unwrap(); | |
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor::default(), None)).unwrap(); | |
// Create and execute the advanced Forward+ framegraph | |
let framegraph = create_advanced_forward_plus_framegraph(); | |
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); | |
{ | |
let mut context = WgpuExecutionContext { | |
device: &device, | |
queue: &queue, | |
encoder: &mut encoder, | |
}; | |
framegraph.execute(&mut context); | |
} | |
queue.submit(Some(encoder.finish())); | |
println!("Advanced Forward+ Framegraph execution complete"); | |
// Visualize the framegraph | |
println!("{}", visualize_framegraph(&framegraph)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment