use std::collections::BTreeMap;
use thiserror::Error;
use crate::graph::CompiledGraph;
use crate::node::{Node, NodeId};
use crate::resource::ResourceId;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ResourceHandle {
Texture(String),
Uniform(String),
Storage(String),
Sampler(String),
External(String),
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ResourceBindings {
inner: BTreeMap<ResourceId, ResourceHandle>,
}
impl ResourceBindings {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with(mut self, id: impl Into<ResourceId>, handle: ResourceHandle) -> Self {
self.inner.insert(id.into(), handle);
self
}
pub fn insert(&mut self, id: impl Into<ResourceId>, handle: ResourceHandle) {
self.inner.insert(id.into(), handle);
}
#[must_use]
pub fn get(&self, id: &ResourceId) -> Option<&ResourceHandle> {
self.inner.get(id)
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&ResourceId, &ResourceHandle)> {
self.inner.iter()
}
}
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum DispatchError {
#[error(
"node {node:?} references resource {resource:?} but no binding was supplied"
)]
MissingBinding {
node: NodeId,
resource: ResourceId,
},
#[error("dispatch backend failure: {0}")]
Backend(String),
}
pub trait Dispatcher {
fn dispatch_node(
&mut self,
node: &Node,
bindings: &ResourceBindings,
) -> Result<(), DispatchError>;
fn dispatch_graph(
&mut self,
graph: &CompiledGraph,
bindings: &ResourceBindings,
) -> Result<(), DispatchError> {
for node in graph.iter_nodes() {
for input in &node.inputs {
if bindings.get(input).is_none() {
return Err(DispatchError::MissingBinding {
node: node.id.clone(),
resource: input.clone(),
});
}
}
for output in &node.outputs {
if bindings.get(output).is_none() {
return Err(DispatchError::MissingBinding {
node: node.id.clone(),
resource: output.clone(),
});
}
}
self.dispatch_node(node, bindings)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RecordedDispatch {
pub node_id: NodeId,
pub inputs: Vec<(ResourceId, ResourceHandle)>,
pub outputs: Vec<(ResourceId, ResourceHandle)>,
}
#[derive(Debug, Clone, Default)]
pub struct RecordingDispatcher {
tape: Vec<RecordedDispatch>,
}
impl RecordingDispatcher {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn tape(&self) -> &[RecordedDispatch] {
&self.tape
}
#[must_use]
pub fn len(&self) -> usize {
self.tape.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.tape.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &RecordedDispatch> {
self.tape.iter()
}
}
impl Dispatcher for RecordingDispatcher {
fn dispatch_node(
&mut self,
node: &Node,
bindings: &ResourceBindings,
) -> Result<(), DispatchError> {
let inputs = node
.inputs
.iter()
.map(|id| {
let handle = bindings
.get(id)
.ok_or_else(|| DispatchError::MissingBinding {
node: node.id.clone(),
resource: id.clone(),
})?
.clone();
Ok((id.clone(), handle))
})
.collect::<Result<Vec<_>, DispatchError>>()?;
let outputs = node
.outputs
.iter()
.map(|id| {
let handle = bindings
.get(id)
.ok_or_else(|| DispatchError::MissingBinding {
node: node.id.clone(),
resource: id.clone(),
})?
.clone();
Ok((id.clone(), handle))
})
.collect::<Result<Vec<_>, DispatchError>>()?;
self.tape.push(RecordedDispatch {
node_id: node.id.clone(),
inputs,
outputs,
});
Ok(())
}
}