use anyhow::Context;
use hecs::World;
use ivy_graphics::Renderer;
use ivy_resources::{Handle, Resources};
use ivy_vulkan::{
commands::CommandBuffer, shaderpass::ShaderPass, vk::Buffer, vk::ClearValue, ClearValueExt,
ImageLayout, LoadOp, PassInfo, StoreOp, Texture,
};
use std::{any::type_name, marker::PhantomData};
pub trait Node: 'static + Send {
fn color_attachments(&self) -> &[AttachmentInfo] {
&[]
}
fn read_attachments(&self) -> &[Handle<Texture>] {
&[]
}
fn input_attachments(&self) -> &[Handle<Texture>] {
&[]
}
fn depth_attachment(&self) -> Option<&AttachmentInfo> {
None
}
fn buffer_reads(&self) -> &[Buffer] {
&[]
}
fn buffer_writes(&self) -> &[Buffer] {
&[]
}
fn clear_values(&self) -> &[ClearValue] {
&[]
}
fn node_kind(&self) -> NodeKind;
fn debug_name(&self) -> &'static str;
fn execute(
&mut self,
world: &mut World,
resources: &Resources,
cmd: &CommandBuffer,
pass_info: &PassInfo,
current_frame: usize,
) -> anyhow::Result<()>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NodeKind {
Graphics,
Transfer,
}
#[derive(Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct AttachmentInfo {
pub store_op: StoreOp,
pub load_op: LoadOp,
pub initial_layout: ImageLayout,
pub final_layout: ImageLayout,
pub resource: Handle<Texture>,
pub clear_value: ClearValue,
}
impl Default for AttachmentInfo {
fn default() -> Self {
Self {
store_op: StoreOp::STORE,
load_op: LoadOp::DONT_CARE,
initial_layout: ImageLayout::UNDEFINED,
final_layout: ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
resource: Handle::null(),
clear_value: ClearValue::default(),
}
}
}
impl AttachmentInfo {
pub fn color(resource: Handle<Texture>) -> Self {
Self {
store_op: StoreOp::STORE,
load_op: LoadOp::CLEAR,
initial_layout: ImageLayout::UNDEFINED,
final_layout: ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
clear_value: ClearValue::color(0.0, 0.0, 0.0, 0.0),
resource,
}
}
pub fn depth_discard(resource: Handle<Texture>) -> Self {
Self {
store_op: StoreOp::DONT_CARE,
load_op: LoadOp::CLEAR,
initial_layout: ImageLayout::UNDEFINED,
final_layout: ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
clear_value: ClearValue::depth_stencil(1.0, 0),
resource,
}
}
pub fn depth_store(resource: Handle<Texture>) -> Self {
Self {
store_op: StoreOp::STORE,
load_op: LoadOp::CLEAR,
initial_layout: ImageLayout::UNDEFINED,
final_layout: ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
clear_value: ClearValue::depth_stencil(1.0, 0),
resource,
}
}
}
pub struct RenderNode<Pass, T> {
renderer: Handle<T>,
marker: PhantomData<Pass>,
}
impl<Pass, T> RenderNode<Pass, T> {
pub fn new(renderer: Handle<T>) -> Self {
Self {
renderer,
marker: PhantomData,
}
}
}
impl<Pass, T, E> Node for RenderNode<Pass, T>
where
Pass: ShaderPass,
T: 'static + Renderer<Error = E> + Send + Sync,
E: Into<anyhow::Error>,
{
fn node_kind(&self) -> NodeKind {
NodeKind::Graphics
}
fn execute(
&mut self,
world: &mut World,
resources: &Resources,
cmd: &CommandBuffer,
pass_info: &PassInfo,
current_frame: usize,
) -> anyhow::Result<()> {
resources
.get_mut(self.renderer)
.with_context(|| format!("Failed to borrow {:?} mutably", type_name::<T>()))?
.draw::<Pass>(world, resources, cmd, &[], pass_info, &[], current_frame)
.map_err(|e| e.into())
.with_context(|| format!("Failed to draw using {:?}", type_name::<T>()))
}
fn debug_name(&self) -> &'static str {
std::any::type_name::<RenderNode<Pass, T>>()
}
}