1use super::geometry::Vertex;
12use super::pixel::SlotBackend;
13use super::stage::{Bindings, Constants};
14use super::tessellator::{
15 Factors, OutputPrimitive, Tessellation, tessellate_isolines, tessellate_quad, tessellate_triangle,
16};
17use super::{Bound, Executable, Runner, Scratch};
18use crate::Sampler;
19use iridia::{
20 DomainFacts, FunctionSig, HullFacts, Program, StageFacts, TessDomain, TessTopology, stage_functions,
21};
22
23#[derive(Clone, Debug, PartialEq)]
25pub struct Patch {
26 pub control_points: Vec<Vertex>,
28 pub factors: Factors,
30 pub vertices: Vec<Vertex>,
33 pub locations: Vec<[f32; 2]>,
35 pub topology: TessTopology,
38 pub indices: Vec<u32>,
40}
41
42#[derive(Debug)]
44pub struct TessellationShader {
45 hull: Program,
46 hull_bindings: Bindings,
47 control: Executable,
48 control_sig: FunctionSig,
49 constant: Executable,
50 hull_facts: HullFacts,
51 domain: Program,
52 domain_bindings: Bindings,
53 domain_exe: Executable,
54 domain_sig: FunctionSig,
55 domain_facts: DomainFacts,
56 edge_factors: Vec<usize>,
58 inside_factors: Vec<usize>,
59 constant_map: Vec<usize>,
61 control_map: Vec<usize>,
63}
64
65fn find(sig: &FunctionSig, name: &str) -> Option<usize> {
66 sig.result.iter().position(|r| r.name == name)
67}
68
69impl TessellationShader {
70 pub fn new(hull: Program, domain: Program) -> Result<Self, String> {
73 let StageFacts::Hull(hull_facts) = hull.interface.facts.clone() else {
74 return Err(format!("a {:?} program is not a hull program", hull.stage));
75 };
76 let StageFacts::Domain(domain_facts) = domain.interface.facts.clone() else {
77 return Err(format!("a {:?} program is not a domain program", domain.stage));
78 };
79 if hull_facts.domain != domain_facts.domain {
80 return Err(format!(
81 "the hull program tessellates a {:?} domain, the domain program is for a {:?} one",
82 hull_facts.domain, domain_facts.domain
83 ));
84 }
85 if hull_facts.output_control_points != domain_facts.input_control_points {
86 return Err(format!(
87 "the hull program writes {} control points, the domain program reads {}",
88 hull_facts.output_control_points, domain_facts.input_control_points
89 ));
90 }
91 let mut hull_sigs =
92 stage_functions(&hull.interface)?.ok_or("a hull program without its functions")?;
93 let constant_sig = hull_sigs
94 .iter()
95 .position(|s| s.name == "patch_constant")
96 .map(|i| hull_sigs.swap_remove(i))
97 .ok_or("a hull program without its patch-constant function")?;
98 let control_sig = hull_sigs
99 .into_iter()
100 .find(|s| s.name == "main")
101 .ok_or("a hull program without its control-point function")?;
102 let domain_sig = stage_functions(&domain.interface)?
103 .and_then(|mut f| f.pop())
104 .ok_or("a domain program without its function")?;
105 let control = Executable::function(&hull, "main").map_err(|e| e.to_string())?;
106 let constant = Executable::function(&hull, "patch_constant").map_err(|e| e.to_string())?;
107 let domain_exe = Executable::function(&domain, "main").map_err(|e| e.to_string())?;
108
109 let (edges, insides) = match hull_facts.domain {
110 TessDomain::Triangle => (3, 1),
111 TessDomain::Quad => (4, 2),
112 TessDomain::Isoline => (2, 0),
113 };
114 let factor = |name: &str, k: usize| {
115 let name = format!("{name}{k}");
116 find(&constant_sig, &name)
117 .ok_or_else(|| format!("the hull program's patch constants have no {name}"))
118 };
119 let edge_factors = (0..edges)
120 .map(|k| factor("SV_TessFactor", k))
121 .collect::<Result<Vec<_>, _>>()?;
122 let inside_factors = (0..insides)
123 .map(|k| factor("SV_InsideTessFactor", k))
124 .collect::<Result<Vec<_>, _>>()?;
125
126 let constants_read = domain_facts.patch_constants.len();
128 let constant_map = domain_sig
129 .arguments
130 .iter()
131 .take(constants_read)
132 .map(|a| {
133 find(&constant_sig, &a.name).ok_or_else(|| {
134 format!(
135 "the domain program reads the patch constant {} the hull program does not write",
136 a.name
137 )
138 })
139 })
140 .collect::<Result<Vec<_>, _>>()?;
141 let control_map = domain_sig
142 .arguments
143 .iter()
144 .skip(constants_read)
145 .take(domain.interface.inputs.len())
146 .map(|a| {
147 find(&control_sig, &a.name).ok_or_else(|| {
148 format!("the domain program reads the control-point varying {} the hull program does not write", a.name)
149 })
150 })
151 .collect::<Result<Vec<_>, _>>()?;
152
153 Ok(Self {
154 hull_bindings: Bindings::new(&hull)?,
155 domain_bindings: Bindings::new(&domain)?,
156 hull,
157 control,
158 control_sig,
159 constant,
160 hull_facts,
161 domain,
162 domain_exe,
163 domain_sig,
164 domain_facts,
165 edge_factors,
166 inside_factors,
167 constant_map,
168 control_map,
169 })
170 }
171
172 pub fn hull(&self) -> &Program {
174 &self.hull
175 }
176
177 pub fn domain(&self) -> &Program {
179 &self.domain
180 }
181
182 pub fn hull_facts(&self) -> &HullFacts {
184 &self.hull_facts
185 }
186
187 pub fn domain_facts(&self) -> &DomainFacts {
189 &self.domain_facts
190 }
191
192 pub fn inputs(&self) -> impl Iterator<Item = &str> {
195 let n = self.hull.interface.inputs.len();
196 self.control_sig.arguments.iter().take(n).map(|a| a.name.as_str())
197 }
198
199 pub fn control_outputs(&self) -> impl Iterator<Item = &str> {
201 let n = self.hull.interface.outputs.len();
202 self.control_sig.result.iter().take(n).map(|a| a.name.as_str())
203 }
204
205 pub fn outputs(&self) -> impl Iterator<Item = &str> {
207 self.domain_sig.result.iter().map(|a| a.name.as_str())
208 }
209
210 pub fn tessellate(
214 &self,
215 hull_constants: &Constants,
216 domain_constants: &Constants,
217 sampler: &mut dyn Sampler,
218 control_points: &[Vertex],
219 primitive: u32,
220 ) -> Result<Patch, String> {
221 let input_count = self.hull_facts.input_control_points as usize;
222 if control_points.len() != input_count {
223 return Err(format!(
224 "{} control points given, the hull program reads {input_count}",
225 control_points.len()
226 ));
227 }
228 let inputs = self.hull.interface.inputs.len();
229 let mut inputs_as_arrays = Vec::with_capacity(inputs.saturating_mul(input_count));
230 for varying in 0..inputs {
231 for (k, point) in control_points.iter().enumerate() {
232 inputs_as_arrays.push(*point.get(varying).ok_or_else(|| {
233 format!(
234 "control point {k} has {} registers, the hull program reads {inputs} ({})",
235 point.len(),
236 self.inputs().collect::<Vec<_>>().join(", ")
237 )
238 })?);
239 }
240 }
241
242 let hull_bound = self.hull_bindings.bind(&self.control, hull_constants)?;
244 let constant_bound = self.hull_bindings.bind(&self.constant, hull_constants)?;
245 let mut hull_backend = self.hull_bindings.backend(sampler);
246 let output_count = self.hull_facts.output_control_points as usize;
247 let outputs = self.hull.interface.outputs.len();
248 let mut control_outputs: Vec<Vertex> = Vec::with_capacity(output_count);
249 let mut scratch = Scratch::new(&self.control);
250 for id in 0..output_count {
251 let mut arguments = inputs_as_arrays.clone();
252 arguments.push([u32::try_from(id).unwrap_or(u32::MAX), 0, 0, 0]);
253 let (values, kept) = call(&self.control, &hull_bound, &mut hull_backend, scratch, &arguments)?;
254 scratch = kept;
255 let members = values.get(..outputs).ok_or_else(|| {
256 format!(
257 "the hull program returned {} registers for a control point, its signature has {outputs}",
258 values.len()
259 )
260 })?;
261 control_outputs.push(members.to_vec());
262 }
263 let mut arguments = inputs_as_arrays;
264 for varying in 0..outputs {
265 for point in &control_outputs {
266 arguments.push(output_of(point, varying)?);
267 }
268 }
269 arguments.push([primitive, 0, 0, 0]);
270 let (constants, _) = call(
271 &self.constant,
272 &constant_bound,
273 &mut hull_backend,
274 Scratch::new(&self.constant),
275 &arguments,
276 )?;
277 let float = |k: usize| -> Result<f32, String> { Ok(f32::from_bits(output_of(&constants, k)?[0])) };
278 let mut factors = Factors {
279 edges: [0.0; 4],
280 inside: [0.0; 2],
281 };
282 for (slot, &k) in factors.edges.iter_mut().zip(&self.edge_factors) {
283 *slot = float(k)?;
284 }
285 for (slot, &k) in factors.inside.iter_mut().zip(&self.inside_factors) {
286 *slot = float(k)?;
287 }
288
289 let partitioning = self.hull_facts.partitioning;
291 let output = match self.hull_facts.topology {
292 TessTopology::Point => OutputPrimitive::Point,
293 TessTopology::Line => OutputPrimitive::Line,
294 TessTopology::TriangleCw => OutputPrimitive::TriangleCw,
295 TessTopology::TriangleCcw => OutputPrimitive::TriangleCcw,
296 };
297 let Tessellation { points, indices } = match self.hull_facts.domain {
298 TessDomain::Triangle => tessellate_triangle(partitioning, output, factors),
299 TessDomain::Quad => tessellate_quad(partitioning, output, factors),
300 TessDomain::Isoline => {
301 tessellate_isolines(partitioning, output, factors.edges[0], factors.edges[1])
302 }
303 };
304
305 let domain_bound = self.domain_bindings.bind(&self.domain_exe, domain_constants)?;
307 let mut domain_backend = self.domain_bindings.backend(sampler);
308 let mut fixed: Vec<[u32; 4]> = self
309 .constant_map
310 .iter()
311 .map(|&k| output_of(&constants, k))
312 .collect::<Result<_, _>>()?;
313 for varying in &self.control_map {
314 for point in &control_outputs {
315 fixed.push(output_of(point, *varying)?);
316 }
317 }
318 let triangle = self.domain_facts.domain == TessDomain::Triangle;
319 let mut vertices = Vec::with_capacity(points.len());
320 let mut scratch = Scratch::new(&self.domain_exe);
321 for &[u, v] in &points {
322 let mut arguments = fixed.clone();
323 let w = if triangle { 1.0 - u - v } else { 0.0 };
324 arguments.push([u.to_bits(), v.to_bits(), w.to_bits(), 0]);
325 arguments.push([primitive, 0, 0, 0]);
326 let (values, kept) = call(
327 &self.domain_exe,
328 &domain_bound,
329 &mut domain_backend,
330 scratch,
331 &arguments,
332 )?;
333 scratch = kept;
334 vertices.push(values);
335 }
336 Ok(Patch {
337 control_points: control_outputs,
338 factors,
339 vertices,
340 locations: points,
341 topology: self.hull_facts.topology,
342 indices,
343 })
344 }
345}
346
347fn output_of(values: &[[u32; 4]], k: usize) -> Result<[u32; 4], String> {
349 values.get(k).copied().ok_or_else(|| {
350 format!(
351 "the program returned {} registers, past register {k}",
352 values.len()
353 )
354 })
355}
356
357fn call(
359 executable: &Executable,
360 bound: &Bound<'_>,
361 backend: &mut SlotBackend<'_>,
362 scratch: Scratch<1>,
363 arguments: &[[u32; 4]],
364) -> Result<(Vec<[u32; 4]>, Scratch<1>), String> {
365 let mut runner = Runner::<1>::with_scratch(executable, bound, backend, None, scratch);
366 let [out] = runner.run([arguments])?;
367 Ok((out.values, runner.into_scratch()))
368}