use crate::kvasir::{ExecutionContext, KvasirNode, ResourceId};
use crate::passes::accessibility::AccessibilityNode;
use crate::passes::backdrop_region::BackdropRegionNode;
use crate::passes::bloom::{BloomBlurNode, BloomExtractNode};
use crate::passes::composite::CompositeNode;
use crate::passes::geometry::GeometryNode;
use crate::passes::glass::{BackdropBlurNode, BackdropCopyNode, GlassNode};
use crate::passes::opaque3d::Opaque3dNode;
use crate::passes::pre_world_panel::PreWorldPanelNode;
use crate::passes::shadow::{DirectionalLight, GpuMesh3d, ShadowNode};
use crate::passes::ui::UINode;
use crate::passes::volumetric::VolumetricNode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PassId {
PreWorldPanel,
Geometry,
BackdropCopy,
BackdropBlur,
Volumetric,
Glass,
UI,
Flow,
ComputeParticle,
BloomExtract,
BloomBlur,
Composite,
Accessibility,
Present,
PostProcess {
pipeline_id: u64,
},
BackdropRegion,
Shadow,
Opaque3d,
}
pub struct PresentNode {
pub inputs: Vec<ResourceId>,
pub outputs: Vec<ResourceId>,
}
impl KvasirNode for PresentNode {
fn label(&self) -> &'static str {
"Present"
}
fn inputs(&self) -> &[ResourceId] {
&self.inputs
}
fn outputs(&self) -> &[ResourceId] {
&self.outputs
}
fn pass_id(&self) -> PassId {
PassId::Present
}
fn execute(&self, _ctx: &mut ExecutionContext) {
}
}
pub const RES_SCENE: ResourceId = ResourceId(1);
pub const RES_SCENE_MSAA: ResourceId = ResourceId(5);
pub const RES_BLUR_A: ResourceId = ResourceId(2);
pub const RES_BLOOM_A: ResourceId = ResourceId(3);
pub const RES_SWAPCHAIN: ResourceId = ResourceId(4);
pub struct RenderGraphConfig<'a> {
pub has_glass: bool,
pub has_bloom: bool,
pub has_accessibility: bool,
pub has_volumetric: bool,
pub active_offscreens: &'a [crate::types::OffscreenEffectConfig],
pub portal_regions: &'a [cvkg_core::Rect],
pub world_space_panels: &'a [(u64, cvkg_vdom::WorldSpacePanel)],
pub width: u32,
pub height: u32,
pub scale: f32,
pub directional_light: Option<DirectionalLight>,
pub mesh_instances_3d: Vec<GpuMesh3d>,
pub scene_radius: f32,
}
pub fn build_render_graph(config: &RenderGraphConfig<'_>) -> super::graph::KvasirGraph {
let mut builder = super::graph::GraphBuilder::new();
let mut panel_outputs = Vec::new();
let mut panel_ids = Vec::new();
for (i, panel) in config.world_space_panels.iter().enumerate() {
let size = panel.1.texture_resolution();
let tex_id = ResourceId(2000 + i as u32);
panel_outputs.push(tex_id);
panel_ids.push(panel.0);
}
if !panel_outputs.is_empty() {
let pre_panel = builder.add_node(Box::new(PreWorldPanelNode::new(panel_outputs, panel_ids)));
}
let geometry = builder.add_node(Box::new(GeometryNode::new()));
let mut last_scene_node = geometry;
for offscreen in config.active_offscreens {
let tex_id = ResourceId(1000 + (offscreen.target_id as u32));
debug_assert!(offscreen.target_id <= u32::MAX as u64, "target_id overflow");
let off_geom = builder.add_node(Box::new(
crate::passes::effects::OffscreenGeometryNode::new(offscreen.target_id, tex_id),
));
let composite =
builder.add_node(Box::new(crate::passes::effects::EffectCompositeNode::new(
offscreen.target_id,
tex_id,
offscreen.effect.clone(),
offscreen.blend_mode,
offscreen.effect_args,
)));
builder.connect(off_geom, tex_id, composite);
builder.connect(last_scene_node, RES_SCENE, composite);
last_scene_node = composite;
}
if config.has_glass {
let copy = builder.add_node(Box::new(BackdropCopyNode::new()));
builder.connect(last_scene_node, RES_SCENE, copy);
let blur = builder.add_node(Box::new(BackdropBlurNode::new(
config.width / 2,
config.height / 2,
)));
builder.connect(copy, RES_BLUR_A, blur);
for (i, region) in config.portal_regions.iter().enumerate() {
let region_id = ResourceId(2000 + i as u32);
let region_node =
builder.add_node(Box::new(BackdropRegionNode::new(*region, region_id)));
builder.connect(last_scene_node, RES_SCENE, region_node);
}
let glass = builder.add_node(Box::new(GlassNode::new(config.scale)));
builder.connect(blur, RES_BLUR_A, glass);
builder.connect(last_scene_node, RES_SCENE, glass);
last_scene_node = glass;
}
let ui = builder.add_node(Box::new(UINode::new()));
builder.connect(last_scene_node, RES_SCENE, ui);
last_scene_node = ui;
let has_volumetric = config.has_volumetric;
if has_volumetric {
let volumetric = builder.add_node(Box::new(VolumetricNode::new()));
builder.connect(last_scene_node, RES_SCENE, volumetric);
last_scene_node = volumetric;
}
if let Some(light) = &config.directional_light {
if !config.mesh_instances_3d.is_empty() {
let shadow_rid = ResourceId(10000); let shadow_node = builder.add_node(Box::new(ShadowNode {
light: *light,
shadow_map: shadow_rid,
mesh_instances: config.mesh_instances_3d.clone(),
scene_radius: config.scene_radius,
}));
let opaque_3d_node = builder.add_node(Box::new(Opaque3dNode {
mesh_instances: config.mesh_instances_3d.clone(),
light: *light,
shadow_map: shadow_rid,
}));
builder.connect(shadow_node, shadow_rid, opaque_3d_node);
builder.connect(opaque_3d_node, RES_SCENE, last_scene_node);
last_scene_node = opaque_3d_node;
}
}
let mut last_bloom_node = None;
if config.has_bloom {
let extract = builder.add_node(Box::new(BloomExtractNode::new()));
builder.connect(last_scene_node, RES_SCENE, extract);
let blur = builder.add_node(Box::new(BloomBlurNode::new(
config.width / 2,
config.height / 2,
)));
builder.connect(extract, RES_BLOOM_A, blur);
last_bloom_node = Some(blur);
}
if config.has_accessibility {
let a11y = builder.add_node(Box::new(AccessibilityNode::new()));
builder.connect(last_scene_node, RES_SCENE, a11y);
last_scene_node = a11y;
}
let composite = builder.add_node(Box::new(CompositeNode::new(
config.has_bloom,
!config.has_accessibility,
)));
builder.connect(last_scene_node, RES_SCENE, composite);
if let Some(bloom_node) = last_bloom_node {
builder.connect(bloom_node, RES_BLOOM_A, composite);
}
let present = builder.add_node(Box::new(PresentNode {
inputs: vec![RES_SCENE],
outputs: vec![],
}));
builder.connect(last_scene_node, RES_SCENE, present);
builder.build()
}