retrofire_core/render/
target.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! Render targets such as framebuffers.
//!
//! The typical render target comprises a color buffer, depth buffer,
//! and possible auxiliary buffers. Special render targets can be used,
//! for example, for visibility or occlusion computations.

use crate::math::vary::Vary;
use crate::util::buf::AsMutSlice2;

use super::{
    ctx::Context, raster::Scanline, shader::FragmentShader, stats::Throughput,
};

/// Trait for types that can be used as render targets.
pub trait Target {
    /// Writes a single scanline into `self`.
    ///
    /// Returns count of fragments input and output.
    fn rasterize<V, Fs>(
        &mut self,
        scanline: Scanline<V>,
        frag_shader: &Fs,
        ctx: &Context,
    ) -> Throughput
    where
        V: Vary,
        Fs: FragmentShader<V>;
}

/// Framebuffer, combining a color (pixel) buffer and a depth buffer.
#[derive(Clone)]
pub struct Framebuf<Col, Dep> {
    pub color_buf: Col,
    pub depth_buf: Dep,
}

impl<Col, Dep> Target for Framebuf<Col, Dep>
where
    Col: AsMutSlice2<u32>,
    Dep: AsMutSlice2<f32>,
{
    /// Rasterizes `scanline` into this framebuffer.
    fn rasterize<V, Fs>(
        &mut self,
        mut sl: Scanline<V>,
        fs: &Fs,
        ctx: &Context,
    ) -> Throughput
    where
        V: Vary,
        Fs: FragmentShader<V>,
    {
        let x0 = sl.xs.start;
        let x1 = sl.xs.end.max(x0);
        let cbuf_span = &mut self.color_buf.as_mut_slice2()[sl.y][x0..x1];
        let zbuf_span = &mut self.depth_buf.as_mut_slice2()[sl.y][x0..x1];

        let mut io = Throughput { i: x1 - x0, o: 0 };

        sl.fragments()
            .zip(cbuf_span)
            .zip(zbuf_span)
            .for_each(|((frag, curr_col), curr_z)| {
                let new_z = frag.pos.z();

                if ctx.depth_test(new_z, *curr_z) {
                    if let Some(new_col) = fs.shade_fragment(frag) {
                        if ctx.color_write {
                            io.o += 1;
                            // TODO Blending should happen here
                            *curr_col = new_col.to_argb_u32();
                        }
                        if ctx.depth_write {
                            *curr_z = new_z;
                        }
                    }
                }
            });
        io
    }
}

impl<Buf: AsMutSlice2<u32>> Target for Buf {
    /// Rasterizes `scanline` into this `u32` color buffer.
    /// Does no z-buffering.
    fn rasterize<V, Fs>(
        &mut self,
        mut sl: Scanline<V>,
        fs: &Fs,
        ctx: &Context,
    ) -> Throughput
    where
        V: Vary,
        Fs: FragmentShader<V>,
    {
        let x0 = sl.xs.start;
        let x1 = sl.xs.end.max(x0);
        let mut io = Throughput { i: x1 - x0, o: 0 };
        let cbuf_span = &mut self.as_mut_slice2()[sl.y][x0..x1];

        sl.fragments()
            .zip(cbuf_span)
            .for_each(|(frag, c)| {
                if let Some(color) = fs.shade_fragment(frag) {
                    if ctx.color_write {
                        io.o += 1;
                        *c = color.to_argb_u32();
                    }
                }
            });
        io
    }
}