use std::sync::Arc;
use iridia_interface::{Label, RenderState};
use crate::shader::{ConstantBinding, InputBinding, Shader, StageBinding, TextureSource};
use crate::{Component, Entity, Geometry, World};
#[derive(Clone, Debug)]
pub struct ShaderDerivation {
pub name: String,
pub result: Result<DerivedStage, String>,
}
#[derive(Clone, Debug)]
pub struct DerivedStage {
pub stage: StageBinding,
pub state: RenderState,
pub program: Arc<iridia::Program>,
pub notes: Vec<String>,
pub vertex_input: Option<DerivedInput>,
}
#[derive(Clone, Debug)]
pub struct DerivedInput {
pub target: Label,
pub name: String,
pub program: Arc<iridia::Program>,
pub notes: Vec<String>,
pub inputs: Vec<InputBinding>,
pub constants: Vec<ConstantBinding>,
}
impl Component for ShaderDerivation {
const NAME: &'static str = "core.shader_derivation";
const GEOMETRY: Geometry = Geometry::None;
fn references(&self, out: &mut Vec<Entity>) {
if let Ok(derived) = &self.result {
out.push(derived.stage.shader);
for t in &derived.stage.textures {
if let TextureSource::Texture { texture, .. } = &t.source {
out.push(*texture);
}
}
}
}
fn to_json(&self) -> Result<Option<serde_json::Value>, crate::JsonError> {
let result = match &self.result {
Ok(d) => serde_json::json!({
"shader": d.stage.shader,
"variant": d.stage.variant,
"outputs": d.stage.outputs,
"notes": d.notes,
"vertex_input": d.vertex_input.as_ref().map(|v| serde_json::json!({
"target": v.target, "name": v.name, "notes": v.notes,
})),
}),
Err(why) => serde_json::json!({ "refusal": why }),
};
crate::json::to_value(&serde_json::json!({ "name": self.name, "result": result })).map(Some)
}
fn verify(&self, _entity: Entity, world: &World) -> Result<(), String> {
if self.name.is_empty() {
return Err("derivation names no derivation".into());
}
let Ok(derived) = &self.result else {
return Ok(());
};
if world.get::<Shader>(derived.stage.shader).is_none() {
return Err("derivation names a source stage whose shader entity has no core.shader".into());
}
if derived.program.stage != derived.stage.stage {
return Err("derived program is not a program of its stage's kind".into());
}
derived.program.validate().map_err(|e| format!("{e:?}"))?;
if let Some(input) = &derived.vertex_input {
if input.name.is_empty() {
return Err("derived input names no derivation".into());
}
if !derived
.program
.interface
.inputs
.iter()
.any(|v| v.label == input.target)
{
return Err("derived input supplies no input of the derived program".into());
}
input.program.validate().map_err(|e| format!("{e:?}"))?;
}
Ok(())
}
fn apply_linear(&self, _: &crate::Linear) -> crate::world::Linearity<Self> {
crate::world::Linearity::Invariant
}
}