retrofire_core/render/
target.rs

1//! Render targets such as framebuffers.
2//!
3//! The typical render target comprises a color buffer, depth buffer,
4//! and possible auxiliary buffers. Special render targets can be used,
5//! for example, for visibility or occlusion computations.
6
7use crate::math::vary::Vary;
8use crate::util::buf::AsMutSlice2;
9
10use super::{
11    ctx::Context, raster::Scanline, shader::FragmentShader, stats::Throughput,
12};
13
14/// Trait for types that can be used as render targets.
15pub trait Target {
16    /// Writes a single scanline into `self`.
17    ///
18    /// Returns count of fragments input and output.
19    fn rasterize<V, Fs>(
20        &mut self,
21        scanline: Scanline<V>,
22        frag_shader: &Fs,
23        ctx: &Context,
24    ) -> Throughput
25    where
26        V: Vary,
27        Fs: FragmentShader<V>;
28}
29
30/// Framebuffer, combining a color (pixel) buffer and a depth buffer.
31#[derive(Clone)]
32pub struct Framebuf<Col, Dep> {
33    pub color_buf: Col,
34    pub depth_buf: Dep,
35}
36
37impl<Col, Dep> Target for Framebuf<Col, Dep>
38where
39    Col: AsMutSlice2<u32>,
40    Dep: AsMutSlice2<f32>,
41{
42    /// Rasterizes `scanline` into this framebuffer.
43    fn rasterize<V, Fs>(
44        &mut self,
45        mut sl: Scanline<V>,
46        fs: &Fs,
47        ctx: &Context,
48    ) -> Throughput
49    where
50        V: Vary,
51        Fs: FragmentShader<V>,
52    {
53        let x0 = sl.xs.start;
54        let x1 = sl.xs.end.max(x0);
55        let cbuf_span = &mut self.color_buf.as_mut_slice2()[sl.y][x0..x1];
56        let zbuf_span = &mut self.depth_buf.as_mut_slice2()[sl.y][x0..x1];
57
58        let mut io = Throughput { i: x1 - x0, o: 0 };
59
60        sl.fragments()
61            .zip(cbuf_span)
62            .zip(zbuf_span)
63            .for_each(|((frag, curr_col), curr_z)| {
64                let new_z = frag.pos.z();
65
66                if ctx.depth_test(new_z, *curr_z) {
67                    if let Some(new_col) = fs.shade_fragment(frag) {
68                        if ctx.color_write {
69                            io.o += 1;
70                            // TODO Blending should happen here
71                            *curr_col = new_col.to_argb_u32();
72                        }
73                        if ctx.depth_write {
74                            *curr_z = new_z;
75                        }
76                    }
77                }
78            });
79        io
80    }
81}
82
83impl<Buf: AsMutSlice2<u32>> Target for Buf {
84    /// Rasterizes `scanline` into this `u32` color buffer.
85    /// Does no z-buffering.
86    fn rasterize<V, Fs>(
87        &mut self,
88        mut sl: Scanline<V>,
89        fs: &Fs,
90        ctx: &Context,
91    ) -> Throughput
92    where
93        V: Vary,
94        Fs: FragmentShader<V>,
95    {
96        let x0 = sl.xs.start;
97        let x1 = sl.xs.end.max(x0);
98        let mut io = Throughput { i: x1 - x0, o: 0 };
99        let cbuf_span = &mut self.as_mut_slice2()[sl.y][x0..x1];
100
101        sl.fragments()
102            .zip(cbuf_span)
103            .for_each(|(frag, c)| {
104                if let Some(color) = fs.shade_fragment(frag) {
105                    if ctx.color_write {
106                        io.o += 1;
107                        *c = color.to_argb_u32();
108                    }
109                }
110            });
111        io
112    }
113}