Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
53568cb
Return Stats from Win::run()
jdahlstrom Nov 22, 2025
0c1636e
Make Batch fields public
jdahlstrom Nov 22, 2025
f32427c
Constify XorShift64 from_seed() and next_bits()
jdahlstrom Nov 14, 2025
632efe6
Add Point::approach() method
jdahlstrom Nov 24, 2025
a785b04
Add Vec2::atan() method to get the angle between a vector and the x-axis
jdahlstrom Oct 13, 2025
bb0ad2b
Add Clone and Debug supertraits to Lerp
jdahlstrom Nov 26, 2025
a3630f2
Add pitch-yaw-roll camera transform
jdahlstrom Nov 15, 2025
6b1d094
Add rotate_pyr() matrix constructor from pitch, yaw, roll angles
jdahlstrom Nov 26, 2025
7d2764b
Add documentation field to Cargo.tomls
jdahlstrom Nov 28, 2025
4d09092
Add no_std demo that uses libc directly to print a ppm to stdout
jdahlstrom Nov 8, 2025
52a21d0
Roll-up: add several new methods
jdahlstrom Nov 11, 2025
c3473a8
Add routines for drawing debug visualizations of geometry
jdahlstrom Nov 28, 2025
6c0e914
Add some missing Default impls
jdahlstrom Nov 29, 2025
d8eea75
Add color gradients
jdahlstrom Aug 14, 2025
972e099
Add Intersect trait and ray/plane and ray/bbox intersection tests
jdahlstrom Nov 13, 2025
cf65202
Minor assorted cleanup
jdahlstrom Nov 13, 2025
6c6edb2
Move everything from core::geom to new module core::geom::prim
jdahlstrom Oct 25, 2025
f944d35
Add sphere type and ray-sphere intersection test
jdahlstrom Nov 14, 2025
b4ce9c8
Fix stupid copypaste bug in Mat3::linear()
jdahlstrom Dec 3, 2025
0a20e9e
Add camera matrix accessors
jdahlstrom Nov 13, 2025
f792b87
Stop reexporting unintended stuff in the prelude due to wildcards
jdahlstrom Dec 3, 2025
a66171b
Remove some superfluous .to() calls
jdahlstrom Dec 4, 2025
b6dca27
Add Tri::map() method for mapping vertices
jdahlstrom Dec 4, 2025
125069b
Use static models in solids demo
jdahlstrom Dec 4, 2025
8db055f
Improve Bezier code, comments, and tests
jdahlstrom Dec 5, 2025
2be94c5
Change BezierSpline to take any IntoIterator
jdahlstrom Dec 5, 2025
9fe457a
Implement Hermite splines
jdahlstrom Dec 5, 2025
51f81a6
Add CubicHermite type
jdahlstrom Dec 6, 2025
7af6d91
Improve spline tests
jdahlstrom Dec 7, 2025
cf49faa
Don't clamp t values, rename tangent() to velocity()
jdahlstrom Dec 7, 2025
66e6375
Make the approximate methods free functions, generalize for any Param…
jdahlstrom Dec 7, 2025
8a6d714
Implement Catmull-Rom and B-splines
jdahlstrom Dec 7, 2025
b309dca
Add Euclidean parameterization to splines
jdahlstrom Dec 5, 2025
0211b34
Improve float support
jdahlstrom Dec 8, 2025
6c32cc2
Remove many fp feature gates now that there is a fallback sqrt()
jdahlstrom Dec 8, 2025
d291693
Add missing impl Parametric for CubicHermite
jdahlstrom Dec 8, 2025
9d29fed
Improve Euclidean doc comments
jdahlstrom Dec 8, 2025
3ce9855
Avoid potential stack overflow in integration test
jdahlstrom Dec 8, 2025
1eb293d
Slightly optimize Plane::is_inside()
jdahlstrom Dec 8, 2025
f099e31
Fix Plane::basis(), add test
jdahlstrom Dec 8, 2025
573e10a
Add several benchmarks using the Divan crate
jdahlstrom Nov 14, 2025
2b6a1fa
Change some asserts to debug_asserts
jdahlstrom Nov 14, 2025
261b6d4
Add more ray-bbox bench cases
jdahlstrom Nov 24, 2025
74b54b0
Fix to_spherical and to_cart to match fixed rotation matrices...
jdahlstrom Nov 23, 2025
40da420
Manual impl Debug for Polar and Spherical
jdahlstrom Nov 23, 2025
0ddd486
Add ray-plane intersection benchmarks, greatly optimize the algorithm
jdahlstrom Dec 8, 2025
f53c3d4
Various binary size optimizations
jdahlstrom Dec 10, 2025
c11b8c7
Add lots of missing #[inline]s to Angle
jdahlstrom Dec 10, 2025
2c1d886
Fix imports in no_std demo
jdahlstrom Dec 10, 2025
37057cc
De-genericize pnm::parse_num() as only u16 parsing is really needed
jdahlstrom Dec 10, 2025
0155368
Add non-method dot() operating on arrays
jdahlstrom Dec 11, 2025
4d4ce58
Add methods to convert to homogeneous coordinates
jdahlstrom Dec 11, 2025
cce0d4f
Ensure that plane coefficients are always in normalized form
jdahlstrom Dec 12, 2025
a5bb2b7
Fix handedness of curses demo
jdahlstrom Dec 15, 2025
1cfea26
Impl Neg for Point now that it has Mul too
jdahlstrom Dec 4, 2025
898dbe7
Add helper function to avoid awkward translate() calls
jdahlstrom Dec 4, 2025
aa46e65
Add 2D line type
jdahlstrom Nov 13, 2025
a1b05e4
Add intersection tests for several 2D types
jdahlstrom Nov 13, 2025
8faf138
Further simplify Plane methods now that coefficients are always norma…
jdahlstrom Dec 21, 2025
3324249
Change As(Mut)Slice2 type parameter to an associated type
jdahlstrom Jan 1, 2026
1f3a668
Change wasm frontend to use [u8; 4] pixel format
jdahlstrom Jan 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ jobs:
- name: Build workspace, std
run: cargo build --workspace --verbose --all-targets --all-features
- name: Run tests, std
run: cargo test --workspace --verbose --features "std" --exclude 'retrofire-demos*'
run: cargo test --workspace --verbose --features "std" --exclude 'retrofire-*demo*'
10 changes: 5 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/target
/Cargo.lock
/.idea
/.vscode
/tmp
target/
Cargo.lock
.idea/
.vscode/
tmp/
*.iml
*.DS_Store

26 changes: 22 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ license.workspace = true
keywords.workspace = true
categories.workspace = true
repository.workspace = true
documentation.workspace = true

[workspace]
members = [
Expand All @@ -27,7 +28,7 @@ members = [
"geom",
"front",
"demos",
"demos/wasm"
"demos/wasm",
]
resolver = "2"

Expand All @@ -36,9 +37,10 @@ edition = "2024"
version = "0.4.0"
authors = ["Johannes 'Sharlin' Dahlström <johannes.dahlstrom@gmail.com>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/jdahlstrom/retrofire"
keywords = ["graphics", "gamedev", "demoscene", "retrocomputing", "rendering"]
categories = ["graphics", "game-development", "no-std"]
repository = "https://github.com/jdahlstrom/retrofire"
documentation = "https://crates.io/crates/retrofire"

[workspace.lints]
clippy.manual_range_contains = "allow"
Expand All @@ -53,17 +55,33 @@ retrofire-core = { version = "0.4.0", path = "core" }
retrofire-front = { version = "0.4.0", path = "front" }
retrofire-geom = { version = "0.4.0", path = "geom" }

[dev-dependencies]
divan = "0.1.21"

[profile.release]
opt-level = 2
codegen-units = 1
codegen-units = 4
lto = "thin"
debug = 1

[profile.bench]
opt-level = 3
codegen-units = 1
lto = "fat"
lto = "thin"

[profile.dev]
opt-level = 1
split-debuginfo = "unpacked"


[[bench]]
name = "fill"
harness = false

[[bench]]
name = "clip"
harness = false

[[bench]]
name = "isect"
harness = false
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ for custom allocators is planned in order to make `alloc` optional as well.
* Type-tagged affine and linear transforms and projections
* Perspective-correct texture mapping
* Triangle mesh data structure and a library of shapes
* Cubic Bézier, Hermite, Catmull–Rom, and B-splines
* Simple random number generation and distributions
* Simple text rendering with bitmap fonts
* Fully customizable rasterization stage
* Collecting rendering performance data
* Reading and writing pnm image files
* Reading and writing Wavefront .obj files
* Cubic Bezier curves and splines
* Simple random number generation and distributions
* Minifb, SDL2, and Wasm frontends
* Forever emoji-free README and docs
* Forever LLM-free code
Expand Down
91 changes: 91 additions & 0 deletions benches/clip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//! Triangle clipping benchmarks.

use core::{array, iter::repeat_with};

use divan::{Bencher, counter::ItemsCount};

use retrofire_core::{
geom::{Tri, vertex},
math::rand::{DEFAULT_RNG, DefaultRng, Distrib},
math::{orthographic, pt3},
render::clip::{ClipVert, view_frustum},
};

//#[global_allocator]
//static ALLOC: AllocProfiler = AllocProfiler::system();

#[divan::bench(args = [1, 10, 100, 1000, 10_000])]
fn clip_mixed(b: Bencher, n: usize) {
let rng = &mut DefaultRng::default();
let pts = pt3(-10.0, -10.0, -10.0)..pt3(10.0, 10.0, 10.0);
let proj = orthographic(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0));

b.with_inputs(|| {
repeat_with(|| {
let vs = array::from_fn(|_| {
ClipVert::new(vertex(proj.apply(&pts.sample(rng)), ()))
});
Tri(vs)
})
.take(n)
.collect::<Vec<_>>()
})
.input_counter(|tris| ItemsCount::of_iter(tris))
.bench_local_values(|tris| {
let mut out = Vec::new();
view_frustum::clip(tris.as_slice(), &mut out);
out
})
}

#[divan::bench(args = [1, 10, 100, 1000, 10_000])]
fn clip_all_inside(b: Bencher, n: usize) {
let rng = &mut DefaultRng::default();
let pts = pt3(-1.0, -1.0, -1.0)..pt3(1.0, 1.0, 1.0);
let proj = orthographic(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0));

b.with_inputs(|| {
repeat_with(|| {
let vs = array::from_fn(|_| {
ClipVert::new(vertex(proj.apply(&pts.sample(rng)), ()))
});
Tri(vs)
})
.take(n)
.collect::<Vec<_>>()
})
.input_counter(|tris| ItemsCount::of_iter(tris))
.bench_local_values(|tris| {
let mut out = Vec::new();
view_frustum::clip(tris.as_slice(), &mut out);
out
})
}

#[divan::bench(args = [1, 10, 100, 1000, 10_000])]
fn clip_all_outside(b: Bencher, n: usize) {
let mut rng = DEFAULT_RNG;
let pts = pt3(2.0, -10.0, -10.0)..pt3(10.0, 10.0, 10.0);
let proj = orthographic(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0));

b.with_inputs(|| {
repeat_with(|| {
let vs = ([pts.start; 3]..[pts.end; 3])
.sample(&mut rng)
.map(|pt| ClipVert::new(vertex(proj.apply(&pt), ())));
Tri(vs)
})
.take(n)
.collect::<Vec<_>>()
})
.input_counter(|tris| ItemsCount::of_iter(tris))
.bench_local_values(|tris| {
let mut out = Vec::with_capacity(tris.len());
view_frustum::clip(tris.as_slice(), &mut out);
out
})
}

fn main() {
divan::main()
}
101 changes: 101 additions & 0 deletions benches/fill.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//! Fillrate benchmarks.

use core::iter::zip;

use divan::{Bencher, counter::ItemsCount};

use retrofire_core::{
geom::{Tri, vertex},
math::{Color3, Color3f, color::gray, pt3, rgb},
render::{
Texture, raster::ScreenPt, raster::tri_fill, tex::SamplerRepeatPot, uv,
},
util::{buf::Buf2, pnm::save_ppm},
};

const SIZES: [f32; 5] = [4.0, 16.0, 64.0, 256.0, 1024.0];

const VERTS: [ScreenPt; 3] =
[pt3(0.1, 0.1, 0.0), pt3(0.9, 0.3, 0.5), pt3(0.4, 0.9, 1.0)];

#[divan::bench(args = SIZES)]
fn flat(b: Bencher, sz: f32) {
let mut buf: Buf2<Color3> = Buf2::new((1024, 1024));

b.with_inputs(|| VERTS.map(|p| vertex(p * sz, ())))
.input_counter(move |vs| ItemsCount::new(Tri(*vs).area() as usize))
.bench_local_values(|vs| {
tri_fill(vs, |sl| {
buf[sl.y][sl.xs].fill(gray(0xCC));
});
});

save_ppm("benches_fill_flat.ppm", buf).unwrap();
}
#[divan::bench(args = SIZES)]
fn gouraud(b: Bencher, sz: f32) {
let mut buf: Buf2<Color3f> = Buf2::new((1024, 1024));

b.with_inputs(|| {
[
vertex(VERTS[0] * sz, rgb(0.9, 0.1, 0.0)),
vertex(VERTS[1] * sz, rgb(0.1, 0.8, 0.1)),
vertex(VERTS[2] * sz, rgb(0.2, 0.3, 1.0)),
]
})
.input_counter(move |vs| ItemsCount::new(Tri(*vs).area() as usize))
.bench_local_values(|vs| {
tri_fill(vs, |sl| {
let y = sl.y;
let xs = sl.xs.clone();
let span = &mut buf[y][xs];

for ((_, col), pix) in zip(sl.vs, span) {
*pix = col;
}
});
});

let buf = Buf2::new_from(
(1024, 1024),
buf.data().into_iter().map(|c| c.to_color3()),
);
save_ppm("benches_fill_color.ppm", buf).unwrap();
}

#[divan::bench(args = SIZES)]
fn texture(b: Bencher, sz: f32) {
let mut buf: Buf2<Color3> = Buf2::new((1024, 1024));

let tex = Texture::from(Buf2::<Color3>::new_from(
(2, 2),
[gray(0xFF), gray(0x33), gray(0x33), gray(0xFF)],
));
let sampler = SamplerRepeatPot::new(&tex);

b.with_inputs(|| {
[
vertex(VERTS[0] * sz, uv(0.0, 0.0)),
vertex(VERTS[1] * sz, uv(4.0, 0.0)),
vertex(VERTS[2] * sz, uv(0.0, 4.0)),
]
})
.input_counter(move |vs| ItemsCount::new(Tri(*vs).area() as usize))
.bench_local_values(|vs| {
tri_fill(vs, |sl| {
let y = sl.y;
let xs = sl.xs.clone();
let span = &mut buf[y][xs];

for ((_, uv), pix) in zip(sl.vs, span) {
*pix = sampler.sample(&tex, uv);
}
});
});

save_ppm("benches_fill_tex.ppm", buf).unwrap();
}

fn main() {
divan::main()
}
Loading