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::{Color3, Color4, Vary};
8use crate::util::{
9    buf::{AsMutSlice2, Buf2, MutSlice2},
10    pixfmt::IntoPixel,
11};
12
13use super::{Context, FragmentShader, raster::Scanline, stats::Throughput};
14
15/// Trait for types that can be used as render targets.
16pub trait Target {
17    /// Writes a single scanline into `self`.
18    ///
19    /// Returns count of fragments input and output.
20    fn rasterize<V, Fs>(
21        &mut self,
22        scanline: Scanline<V>,
23        frag_shader: &Fs,
24        ctx: &Context,
25    ) -> Throughput
26    where
27        V: Vary,
28        Fs: FragmentShader<V>;
29}
30
31/// Framebuffer, combining a color (pixel) buffer and a depth buffer.
32#[derive(Clone)]
33pub struct Framebuf<Col, Dep> {
34    pub color_buf: Col,
35    pub depth_buf: Dep,
36}
37
38/// Color buffer with a specified pixel format.
39pub struct Colorbuf<B, F> {
40    pub buf: B,
41    pub fmt: F,
42}
43
44impl<B, F: Default> Colorbuf<B, F> {
45    pub fn new(buf: B) -> Self {
46        Self { buf, fmt: F::default() }
47    }
48}
49
50impl<T, B: AsMutSlice2<T>, F> AsMutSlice2<T> for Colorbuf<B, F> {
51    fn as_mut_slice2(&mut self) -> MutSlice2<'_, T> {
52        self.buf.as_mut_slice2()
53    }
54}
55
56impl<Col, Fmt, Dep> Target for Framebuf<Colorbuf<Col, Fmt>, Dep>
57where
58    Col: AsMutSlice2<u32>,
59    Dep: AsMutSlice2<f32>,
60    Color4: IntoPixel<u32, Fmt>,
61{
62    /// Rasterizes `scanline` into this framebuffer.
63    fn rasterize<V: Vary, Fs: FragmentShader<V>>(
64        &mut self,
65        sl: Scanline<V>,
66        fs: &Fs,
67        ctx: &Context,
68    ) -> Throughput {
69        rasterize_fb(
70            &mut self.color_buf,
71            &mut self.depth_buf,
72            sl,
73            fs,
74            Color4::into_pixel,
75            ctx,
76        )
77    }
78}
79
80impl<Buf, Fmt> Target for Colorbuf<Buf, Fmt>
81where
82    Buf: AsMutSlice2<u32>,
83    Color4: IntoPixel<u32, Fmt>,
84{
85    /// Rasterizes `scanline` into this `u32` color buffer.
86    /// Does no z-buffering.
87    fn rasterize<V: Vary, Fs: FragmentShader<V>>(
88        &mut self,
89        sl: Scanline<V>,
90        fs: &Fs,
91        ctx: &Context,
92    ) -> Throughput {
93        rasterize(&mut self.buf, sl, fs, Color4::into_pixel, ctx)
94    }
95}
96
97impl Target for Buf2<Color4> {
98    fn rasterize<V: Vary, Fs: FragmentShader<V>>(
99        &mut self,
100        sl: Scanline<V>,
101        fs: &Fs,
102        ctx: &Context,
103    ) -> Throughput {
104        rasterize(self, sl, fs, |c| c, ctx)
105    }
106}
107
108impl Target for Buf2<Color3> {
109    fn rasterize<V: Vary, Fs: FragmentShader<V>>(
110        &mut self,
111        sl: Scanline<V>,
112        fs: &Fs,
113        ctx: &Context,
114    ) -> Throughput {
115        rasterize(self, sl, fs, |c| c.to_rgb(), ctx)
116    }
117}
118
119pub fn rasterize<T, V: Vary>(
120    buf: &mut impl AsMutSlice2<T>,
121    mut sl: Scanline<V>,
122    fs: &impl FragmentShader<V>,
123    mut conv: impl FnMut(Color4) -> T,
124    ctx: &Context,
125) -> Throughput {
126    let x0 = sl.xs.start;
127    let x1 = sl.xs.end.max(x0);
128    let mut io = Throughput { i: x1 - x0, o: 0 };
129    let cbuf_span = &mut buf.as_mut_slice2()[sl.y][x0..x1];
130
131    sl.fragments()
132        .zip(cbuf_span)
133        .for_each(|(frag, curr_col)| {
134            if let Some(new_col) = fs.shade_fragment(frag)
135                && ctx.color_write
136            {
137                io.o += 1;
138                *curr_col = conv(new_col);
139            }
140        });
141    io
142}
143
144pub fn rasterize_fb<T, V: Vary>(
145    cbuf: &mut impl AsMutSlice2<T>,
146    zbuf: &mut impl AsMutSlice2<f32>,
147    mut sl: Scanline<V>,
148    fs: &impl FragmentShader<V>,
149    mut conv: impl FnMut(Color4) -> T,
150    ctx: &Context,
151) -> Throughput {
152    let x0 = sl.xs.start;
153    let x1 = sl.xs.end.max(x0);
154    let cbuf_span = &mut cbuf.as_mut_slice2()[sl.y][x0..x1];
155    let zbuf_span = &mut zbuf.as_mut_slice2()[sl.y][x0..x1];
156
157    let mut io = Throughput { i: x1 - x0, o: 0 };
158
159    sl.fragments()
160        .zip(cbuf_span)
161        .zip(zbuf_span)
162        .for_each(|((frag, curr_col), curr_z)| {
163            let new_z = frag.pos.z();
164
165            if ctx.depth_test(new_z, *curr_z)
166                && let Some(new_col) = fs.shade_fragment(frag)
167            {
168                if ctx.color_write {
169                    io.o += 1;
170                    // TODO Blending should happen here
171                    *curr_col = conv(new_col);
172                }
173                if ctx.depth_write {
174                    *curr_z = new_z;
175                }
176            }
177        });
178    io
179}