Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 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
9559368
Implement Catmull-Rom and B-splines
jdahlstrom Dec 7, 2025
297b160
Add Euclidean parameterization to splines
jdahlstrom Dec 5, 2025
80db623
Improve float support
jdahlstrom Dec 8, 2025
97a7b38
Remove many fp feature gates now that there is a fallback sqrt()
jdahlstrom Dec 8, 2025
6dd9124
Add missing impl Parametric for CubicHermite
jdahlstrom Dec 8, 2025
b763f3e
Improve Euclidean doc comments
jdahlstrom Dec 8, 2025
39a36ff
Avoid potential stack overflow in integration test
jdahlstrom Dec 8, 2025
6dfe207
Slightly optimize Plane::is_inside()
jdahlstrom Dec 8, 2025
815df2c
Fix Plane::basis(), add test
jdahlstrom Dec 8, 2025
bcc168a
Add several benchmarks using the Divan crate
jdahlstrom Nov 14, 2025
67f16ca
Change some asserts to debug_asserts
jdahlstrom Nov 14, 2025
6e4d0cd
Add more ray-bbox bench cases
jdahlstrom Nov 24, 2025
131c8ba
Fix to_spherical and to_cart to match fixed rotation matrices...
jdahlstrom Nov 23, 2025
77d1d71
Manual impl Debug for Polar and Spherical
jdahlstrom Nov 23, 2025
37945df
WIP Add arg/min/max methods to vectors
jdahlstrom Oct 25, 2025
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
Loading