1use std::array;
2use std::collections::LinkedList;
3use std::error::Error;
4use std::fmt::{self, Display, Formatter};
5use std::sync::{Arc, Mutex};
6
7use nalgebra::{Point2, Point3, Vector2};
8use rayon::prelude::*;
9
10use super::blending::Blendable;
11use super::framebuffer::{Framebuffer, MutableScanline};
12use super::scissor::Scissor;
13use super::shader::{FragmentContext, Shader, VertexContext, VertexOutput};
14
15#[derive(Debug)]
16pub enum RasterizerError {
17 NoRenderTarget,
18 RenderTargetUnfinished,
19}
20
21impl Display for RasterizerError {
22 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
23 write!(
24 f,
25 "{}",
26 match self {
27 Self::NoRenderTarget => "No render target pushed to the stack!",
28 Self::RenderTargetUnfinished => "Render target still present on the stack!",
29 }
30 )
31 }
32}
33
34impl Error for RasterizerError {}
35
36#[derive(Debug, Clone, Copy)]
37pub enum BlendFactor {
38 Zero,
39 One,
40 SrcAlpha,
41 OneMinusSrcAlpha,
42 DstAlpha,
43 OneMinusDstAlpha,
44}
45
46#[derive(Debug, Clone, Copy)]
47pub enum BlendOp {
48 Add,
49 SrcSubDst,
50 DstSubSrc,
51}
52
53#[derive(Debug)]
54pub struct ComponentBlendOp {
55 pub op: BlendOp,
56 pub src_factor: BlendFactor,
57 pub dst_factor: BlendFactor,
58}
59
60#[derive(Debug)]
61pub struct BlendAttachment {
62 pub color: Option<ComponentBlendOp>,
63 pub alpha: Option<ComponentBlendOp>,
64}
65
66fn color_to_channels(color: u32) -> [f32; 4] {
67 color.to_be_bytes().map(|c| (c as f32) / 256.0)
68}
69
70fn channels_to_color(channels: [f32; 4]) -> u32 {
71 u32::from_be_bytes(channels.map(|c| (c * 256.0) as u8))
72}
73
74struct BlendContext {
75 src_alpha: f32,
76 dst_alpha: f32,
77}
78
79impl ComponentBlendOp {
80 fn channel_term(value: f32, factor: &BlendFactor, context: &BlendContext) -> f32 {
81 let coeff = match factor {
82 BlendFactor::Zero => 0.0,
83 BlendFactor::One => 1.0,
84 BlendFactor::SrcAlpha => context.src_alpha,
85 BlendFactor::OneMinusSrcAlpha => 1.0 - context.src_alpha,
86 BlendFactor::DstAlpha => context.dst_alpha,
87 BlendFactor::OneMinusDstAlpha => 1.0 - context.dst_alpha,
88 };
89
90 coeff * value
91 }
92
93 fn blend(&self, src: f32, dst: f32, context: &BlendContext) -> f32 {
94 let src_term = Self::channel_term(src, &self.src_factor, context);
95 let dst_term = Self::channel_term(dst, &self.dst_factor, context);
96
97 match &self.op {
98 BlendOp::Add => src_term + dst_term,
99 BlendOp::SrcSubDst => src_term - dst_term,
100 BlendOp::DstSubSrc => dst_term - src_term,
101 }
102 }
103}
104
105impl BlendAttachment {
106 fn blend_colors(&self, src: u32, dst: u32) -> u32 {
107 let src_channels = color_to_channels(src);
108 let dst_channels = color_to_channels(dst);
109
110 let context = BlendContext {
111 src_alpha: src_channels[3],
112 dst_alpha: dst_channels[3],
113 };
114
115 channels_to_color(array::from_fn(|i| {
116 let component_op = match i {
117 3 => &self.alpha,
118 _ => &self.color,
119 };
120
121 match component_op {
122 Some(op) => op.blend(src_channels[i], dst_channels[i], &context),
123 None => src_channels[i],
124 }
125 }))
126 }
127}
128
129#[derive(Debug, Clone, Copy)]
130pub enum DepthMode {
131 DontCare,
132 Test,
133 Write,
134}
135
136impl DepthMode {
137 fn should_test(&self) -> bool {
138 match self {
139 DepthMode::DontCare => false,
140 _ => true,
141 }
142 }
143
144 fn should_write(&self) -> bool {
145 match self {
146 DepthMode::Write => true,
147 _ => false,
148 }
149 }
150}
151
152#[derive(Debug, Clone, Copy)]
153pub enum WindingOrder {
154 Clockwise,
155 CounterClockwise,
156}
157
158#[derive(Debug)]
159pub struct Pipeline<T: Shader> {
160 pub depth: DepthMode,
161
162 pub cull_back: bool,
163 pub winding_order: WindingOrder,
164
165 pub blending: Option<Vec<BlendAttachment>>,
166
167 pub shader: T,
168}
169
170pub struct IndexedRenderCall<'a, T: Shader> {
171 pub pipeline: &'a Pipeline<T>,
172
173 pub vertex_offset: usize,
174 pub first_instance: usize,
175 pub instance_count: usize,
176
177 pub scissor: Option<Scissor>,
178
179 pub indices: &'a [u16],
180 pub data: &'a T::Uniform,
181}
182
183pub fn gen_scissor(uv: &[Point2<f32>], max_width: usize, max_height: usize) -> Scissor {
184 let mut x0 = max_width;
185 let mut y0 = max_height;
186
187 let mut x1: usize = 0;
188 let mut y1: usize = 0;
189
190 for point in uv {
191 let x = point.x.clamp(0.0, 1.0) * max_width as f32;
192 let y = point.y.clamp(0.0, 1.0) * max_height as f32;
193
194 x0 = (x.floor() as usize).min(x0);
195 y0 = (y.floor() as usize).min(y0);
196
197 x1 = (x.ceil() as usize).max(x1);
198 y1 = (y.ceil() as usize).max(y1);
199 }
200
201 Scissor {
202 x: x0,
203 y: y0,
204 width: x1 - x0,
205 height: y1 - y0,
206 }
207}
208
209fn rotate_cw(v: &Vector2<f32>) -> Vector2<f32> {
210 Vector2::new(-v.y, v.x)
211}
212
213fn rotate_ccw(v: &Vector2<f32>) -> Vector2<f32> {
214 Vector2::new(v.y, -v.x)
215}
216
217fn signed_triangle_area(points: [&Point2<f32>; 3], winding: WindingOrder) -> f32 {
218 let a = points[0];
219 let b = points[1];
220 let c = points[2];
221
222 let ab = b - a;
223 let ac = c - a;
224
225 let normal = match winding {
226 WindingOrder::CounterClockwise => rotate_ccw(&ab),
228
229 WindingOrder::Clockwise => rotate_cw(&ab),
231 };
232
233 ac.dot(&normal) / 2.0
234}
235
236pub const VERTICES_PER_FACE: usize = 3;
237
238struct FragmentInfo {
239 depth: f32,
240 weights: [f32; VERTICES_PER_FACE],
241}
242
243fn process_fragment_geometry<T: Shader>(
244 triangle: &[Point3<f32>; VERTICES_PER_FACE],
245 point: &Point2<f32>,
246 pipeline: &Pipeline<T>,
247) -> Option<FragmentInfo> {
248 let screen_points = triangle.each_ref().map(|p| p.xy());
249 let areas: [_; VERTICES_PER_FACE] = array::from_fn(|i| {
250 let a = &screen_points[(i + 1) % VERTICES_PER_FACE];
251 let b = &screen_points[(i + 2) % VERTICES_PER_FACE];
252
253 signed_triangle_area([a, b, point], pipeline.winding_order)
254 });
255
256 let areas_valid = areas.each_ref().map(|area| *area >= 0.0);
258 let mut should_keep = areas_valid.iter().all(|valid| *valid);
259
260 if !pipeline.cull_back {
261 should_keep |= areas_valid.iter().all(|valid| !*valid);
263 }
264
265 if should_keep {
266 let area_sum = areas.iter().sum::<f32>();
267 let flat_weights = areas.map(|area| area / area_sum);
268
269 let inverse_depths = triangle.each_ref().map(|p| 1.0 / p.z);
270 let inverse_depth = flat_weights
271 .iter()
272 .zip(inverse_depths.iter())
273 .map(|(w, d)| w * d)
274 .sum::<f32>();
275
276 Some(FragmentInfo {
277 depth: 1.0 / inverse_depth,
278 weights: array::from_fn(|i| flat_weights[i] * inverse_depths[i] / inverse_depth),
279 })
280 } else {
281 None
282 }
283}
284
285fn depth_test(x: usize, current_depth: f32, scanline: &MutableScanline) -> bool {
287 if let Some(depth) = &scanline.depth {
288 let closest_depth = depth[x];
289
290 current_depth <= closest_depth
291 } else {
292 true
293 }
294}
295
296struct FaceContext<'a, T: Shader> {
297 instance_id: usize,
298 call: &'a IndexedRenderCall<'a, T>,
299 vertex_output: &'a [VertexOutput<T::Working>; VERTICES_PER_FACE],
300
301 fb_width: usize,
302 fb_height: usize,
303}
304
305fn should_discard_fragment(
306 x: usize,
307 depth_mode: &DepthMode,
308 current_depth: f32,
309 scanline: &MutableScanline,
310) -> bool {
311 if current_depth < 0.0 {
312 true
313 } else {
314 depth_mode.should_test() && !depth_test(x, current_depth, scanline)
315 }
316}
317
318fn render_fragment<T: Shader>(
319 x: usize,
320 context: &FaceContext<T>,
321 scanline: &mut MutableScanline,
322 point: Point2<f32>,
323 frag: FragmentInfo,
324) {
325 let color = context
326 .call
327 .pipeline
328 .shader
329 .fragment_stage(&FragmentContext {
330 instance_id: context.instance_id,
331 position: Point3::new(point.x, point.y, frag.depth),
332 data: &context.call.data,
333 working: T::Working::blend(
334 &context.vertex_output.each_ref().map(|output| &output.data),
335 &frag.weights,
336 ),
337 });
338
339 for i in 0..scanline.color.len() {
340 let row = &mut scanline.color[i];
341
342 row[x] = match &context.call.pipeline.blending {
343 Some(blending) => blending[i].blend_colors(color, row[x]),
344 None => color,
345 };
346 }
347
348 if context.call.pipeline.depth.should_write()
349 && let Some(depth_row) = &mut scanline.depth
350 {
351 depth_row[x] = frag.depth;
352 }
353}
354
355fn process_pixel<T: Shader>(x: usize, context: &FaceContext<T>, scanline: &mut MutableScanline) {
356 let point = Point2::new(
357 (((x as f32 + 0.5) / context.fb_width as f32) * 2.0) - 1.0,
358 (((scanline.y as f32 + 0.5) / context.fb_height as f32) * 2.0) - 1.0,
359 );
360
361 let vertex_positions = context.vertex_output.each_ref().map(|data| data.position);
362 if let Some(frag) = process_fragment_geometry(&vertex_positions, &point, &context.call.pipeline)
363 {
364 if should_discard_fragment(x, &context.call.pipeline.depth, frag.depth, scanline) {
365 return;
366 }
367
368 render_fragment(x, context, scanline, point, frag);
369 }
370}
371
372#[derive(Debug, Default)]
373pub struct RenderStats {
374 pub faces_processed: usize,
375 pub faces_rendered: usize,
376 pub instances: usize,
377 pub calls: usize,
378}
379
380pub struct Rasterizer {
381 stats: RenderStats,
382 render_targets: LinkedList<Arc<Mutex<Framebuffer>>>,
383}
384
385impl Rasterizer {
386 pub fn new() -> Rasterizer {
387 Rasterizer {
388 stats: RenderStats::default(),
389 render_targets: LinkedList::new(),
390 }
391 }
392
393 pub fn new_frame(&mut self) -> Result<(), RasterizerError> {
394 if !self.render_targets.is_empty() {
395 Err(RasterizerError::RenderTargetUnfinished)
396 } else {
397 self.stats = RenderStats::default();
398 Ok(())
399 }
400 }
401
402 pub fn stats<'a>(&'a self) -> &'a RenderStats {
403 &self.stats
404 }
405
406 pub fn push_render_target(&mut self, target: Arc<Mutex<Framebuffer>>) {
407 self.render_targets.push_back(target);
408 }
409
410 pub fn pop_render_target(&mut self) -> Result<(), RasterizerError> {
411 match self.render_targets.pop_back() {
412 Some(_) => Ok(()),
413 None => Err(RasterizerError::NoRenderTarget),
414 }
415 }
416
417 pub fn current_render_target(&mut self) -> Result<Arc<Mutex<Framebuffer>>, RasterizerError> {
418 match self.render_targets.back() {
419 Some(top) => Ok(top.clone()),
420 None => Err(RasterizerError::NoRenderTarget),
421 }
422 }
423
424 fn render_face<T: Shader + Sync>(
425 &mut self,
426 instance_id: usize,
427 face_index: usize,
428 call: &IndexedRenderCall<T>,
429 framebuffer: &mut Framebuffer,
430 ) {
431 let index_offset = face_index * VERTICES_PER_FACE;
432 let (fb_width, fb_height) = framebuffer.size();
433
434 let vertex_output = array::from_fn(|i| {
435 call.pipeline.shader.vertex_stage(&VertexContext {
436 vertex_id: call.indices[index_offset + i] as usize,
437 instance_id: instance_id,
438 data: call.data,
439 })
440 });
441
442 let uv = vertex_output
443 .each_ref()
444 .map(|output| output.position.xy().map(|x| (x + 1.0) / 2.0));
445
446 let generated_scissor = gen_scissor(&uv, fb_width, fb_height);
447 let final_scissor = match &call.scissor {
448 Some(user_scissor) => generated_scissor.intersect_with(user_scissor),
449 None => Some(generated_scissor), };
451
452 if let Some(scissor) = final_scissor {
453 let fc = FaceContext {
454 instance_id,
455 call,
456 vertex_output: &vertex_output,
457 fb_width,
458 fb_height,
459 };
460
461 framebuffer
462 .scanlines(scissor.y, scissor.height)
463 .par_iter_mut()
464 .for_each(|scanline| {
465 for delta_x in 0..scissor.width {
466 process_pixel(scissor.x + delta_x, &fc, scanline);
467 }
468 });
469
470 self.stats.faces_rendered += 1;
471 }
472 }
473
474 pub fn render_indexed<T: Shader + Sync>(
475 &mut self,
476 call: &IndexedRenderCall<T>,
477 ) -> Result<(), RasterizerError> {
478 let face_count = call.indices.len() / VERTICES_PER_FACE;
479
480 let top = self.current_render_target()?;
483 let mut framebuffer = top.lock().unwrap();
484
485 for i in 0..call.instance_count {
486 for j in 0..face_count {
487 self.render_face(call.first_instance + i, j, call, &mut framebuffer);
488 self.stats.faces_processed += 1;
489 }
490
491 self.stats.instances += 1;
492 }
493
494 self.stats.calls += 1;
495 Ok(())
496 }
497}