Skip to main content

primase_core/shader/
facts.rs

1//! The program facts a resolver records, as components: [`ProgramFacts`] bound to its `core.shader` entity.
2use iridia_interface::{D3dBinding, InstructionCount, Interface, ResourceId, Stage, Varying};
3use serde::Serialize;
4
5use super::roles::OutputBinding;
6use super::surface::Surface;
7use crate::{Component, Entity, Geometry, World};
8
9/// Just the declared resource's source address; bulk buffer layouts and default constants stay in the lifted program.
10#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
11pub struct ProgramResourceFacts {
12    /// The resource's id in the program's interface.
13    pub id: ResourceId,
14    /// The name the source gives the resource (`ResourceProvenance::name`).
15    pub name: String,
16    /// The D3D register class, index and space the resource is bound at; `None` when the source has no such address.
17    pub d3d: Option<D3dBinding>,
18}
19
20/// `core.program_facts`: a resolver's measured facts about one program, what it reads and writes and how large it is,
21/// also stored on the program entity for reuse across materials.
22#[derive(Clone, Debug, PartialEq, Serialize)]
23pub struct ProgramFacts {
24    /// The name the consumer knows the program by.
25    pub name: String,
26    /// The `sha256:` hash of the program's bytes, as `Provenance::source_hash` gives it; `None` when the program was not
27    /// read.
28    pub hash: Option<String>,
29    /// The stage the program runs in.
30    pub stage: Stage,
31    /// The consumer's handle to the program; `None` until [`ProgramFacts::bound`] is called.
32    pub shader: Option<Entity>,
33    /// The program's size, or `None` for a program that was not lifted or whose lifter does not count.
34    pub instruction_count: Option<InstructionCount>,
35    /// Used signature elements, including the original register/mask provenance. Not a guess from semantic names.
36    pub inputs: Vec<Varying>,
37    /// Resource declarations retain names/register/space, not layouts or bytecode. Not a resource liveness claim.
38    pub resources: Vec<ProgramResourceFacts>,
39    /// The roles of the program's outputs; empty until [`ProgramFacts::bound`] is called.
40    pub outputs: Vec<OutputBinding>,
41    /// The outputs read by their roles as a lit surface; the default surface until [`ProgramFacts::bound`] is called.
42    pub surface: Surface,
43    /// A program that was not found/selected/lifted has a reason, never a zero instruction count.
44    pub not_lifted: Option<String>,
45}
46impl ProgramFacts {
47    /// The facts of a lifted program, named `name`, of its stage, the hash of its bytes, its instruction count and its
48    /// interface (the inputs it uses and the source address of each resource); no output roles until
49    /// [`ProgramFacts::bound`]. Nothing is parsed or lifted again.
50    pub fn lifted(
51        name: impl Into<String>,
52        stage: Stage,
53        hash: Option<String>,
54        instruction_count: Option<InstructionCount>,
55        interface: &Interface,
56    ) -> Self {
57        Self {
58            name: name.into(),
59            hash,
60            stage,
61            shader: None,
62            instruction_count,
63            inputs: interface
64                .inputs
65                .iter()
66                .filter(|input| input.used)
67                .cloned()
68                .collect(),
69            resources: interface
70                .resources
71                .iter()
72                .map(|resource| ProgramResourceFacts {
73                    id: resource.id,
74                    name: resource.provenance.name.clone(),
75                    d3d: resource.provenance.d3d,
76                })
77                .collect(),
78            outputs: Vec::new(),
79            surface: Surface::default(),
80            not_lifted: None,
81        }
82    }
83    /// The facts of a program that has none: no inputs, resources, outputs or instruction count, and `reason` in
84    /// `not_lifted`.
85    pub fn unavailable(
86        name: impl Into<String>,
87        stage: Stage,
88        hash: Option<String>,
89        reason: impl Into<String>,
90    ) -> Self {
91        Self {
92            name: name.into(),
93            hash,
94            stage,
95            shader: None,
96            instruction_count: None,
97            inputs: Vec::new(),
98            resources: Vec::new(),
99            outputs: Vec::new(),
100            surface: Surface::default(),
101            not_lifted: Some(reason.into()),
102        }
103    }
104    /// Output roles are resolved after the lift; the surface is always the shared reading ([`Surface::read`]).
105    pub fn bound(mut self, shader: Entity, outputs: &[OutputBinding]) -> Self {
106        self.shader = Some(shader);
107        self.outputs = outputs.to_vec();
108        self.surface = Surface::read(self.outputs.iter().map(|output| (output.lanes, &output.role)));
109        self
110    }
111}
112
113impl Component for ProgramFacts {
114    const NAME: &'static str = "core.program_facts";
115    const GEOMETRY: Geometry = Geometry::None;
116    fn to_json(&self) -> Result<Option<serde_json::Value>, crate::json::JsonError> {
117        crate::json::to_value(self).map(Some)
118    }
119    fn references(&self, out: &mut Vec<Entity>) {
120        out.extend(self.shader);
121    }
122    fn verify(&self, _e: Entity, _w: &World) -> Result<(), String> {
123        if self.not_lifted.is_some() && self.instruction_count.is_some() {
124            return Err("an unlifted program claims an instruction count".into());
125        }
126        Ok(())
127    }
128    fn apply_linear(&self, _l: &crate::Linear) -> crate::world::Linearity<Self> {
129        crate::world::Linearity::Invariant
130    }
131}
132/// `core.material_programs`: the selected/resolved program facts on each material, including unresolved programs.
133#[derive(Clone, Debug, Default, PartialEq, Serialize)]
134pub struct MaterialPrograms(pub Vec<ProgramFacts>);
135impl Component for MaterialPrograms {
136    const NAME: &'static str = "core.material_programs";
137    const GEOMETRY: Geometry = Geometry::None;
138    fn to_json(&self) -> Result<Option<serde_json::Value>, crate::json::JsonError> {
139        crate::json::to_value(self).map(Some)
140    }
141    fn references(&self, out: &mut Vec<Entity>) {
142        for program in &self.0 {
143            program.references(out);
144        }
145    }
146    fn verify(&self, e: Entity, w: &World) -> Result<(), String> {
147        for program in &self.0 {
148            program.verify(e, w)?;
149        }
150        Ok(())
151    }
152    fn apply_linear(&self, _l: &crate::Linear) -> crate::world::Linearity<Self> {
153        crate::world::Linearity::Invariant
154    }
155}