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
}
}