use std::time::Instant;
const WIDTH: usize = 1280;
const HEIGHT: usize = 720;
const TILE_SIZE: usize = 16;
fn build_texture() -> Vec<u32> {
let mut tex = vec![0u32; 1024 * 1024];
for y in 0..1024usize {
for x in 0..1024usize {
let r = (x as u8).wrapping_mul(7).wrapping_add(y as u8);
let g = (x as u8).wrapping_mul(11).wrapping_add(y as u8).wrapping_mul(5);
let b = (x as u8).wrapping_mul(2).wrapping_add(y as u8).wrapping_mul(7);
tex[y * 1024 + x] = 0xFF000000
| ((r as u32) << 16)
| ((g as u32) << 8)
| (b as u32);
}
}
tex
}
#[inline(always)]
fn fragment_shader(
px: u32, py: u32, frame: u32,
tex_cache: &[u32; TILE_SIZE * TILE_SIZE],
depth_buf: &mut [f32],
color_buf: &mut [u32],
) {
let tx = (px & 0xF) as usize; let ty = (py & 0xF) as usize;
let texel = tex_cache[ty * TILE_SIZE + tx];
let tr = ((texel >> 16) & 0xFF) as f32 / 255.0_f32;
let tg = ((texel >> 8) & 0xFF) as f32 / 255.0_f32;
let tb = (texel & 0xFF) as f32 / 255.0_f32;
let x_f = px as f32 / WIDTH as f32;
let y_f = py as f32 / HEIGHT as f32;
let t = frame as f32 * 0.01_f32;
let nx = (x_f * 4.0 + t).sin();
let ny = (y_f * 4.0 + t).cos();
let mut total_intensity: f32 = 0.0_f32;
for l in 0..4i32 {
let angle = (t * 3.14159_f32 / 2.0_f32) + (l as f32) * 1.57079_f32;
let lx = angle.cos();
let ly = angle.sin();
let dot_l = (nx * lx + ny * ly).max(0.0_f32);
total_intensity += dot_l * 0.4_f32;
}
let r = (tr * total_intensity * 255.0_f32) as u32;
let g = (tg * total_intensity * 255.0_f32) as u32;
let b = (tb * total_intensity * 255.0_f32) as u32;
let depth = (py as f32 / HEIGHT as f32) + (t.sin() * 0.1_f32);
let buf_idx = py as usize * WIDTH + px as usize;
if depth < depth_buf[buf_idx] {
depth_buf[buf_idx] = depth;
color_buf[buf_idx] = (255u32 << 24) | (r.min(255) << 16) | (g.min(255) << 8) | b.min(255);
}
}
#[inline(never)]
fn render_tile(
tile_x: usize, tile_y: usize, frame: u32,
texture: &[u32],
color_buf: &mut [u32],
depth_buf: &mut [f32],
) {
let mut tex_cache = [0u32; TILE_SIZE * TILE_SIZE];
for y in 0..TILE_SIZE {
for x in 0..TILE_SIZE {
let gx = tile_x as u32 * TILE_SIZE as u32 + x as u32;
let gy = tile_y as u32 * TILE_SIZE as u32 + y as u32;
let uv_x = ((gx as f32 * 4.0 + frame as f32 * 0.01_f32).sin()
* 0.5 + 0.5) * 1024.0_f32;
let uv_y = ((gy as f32 * 4.0 + frame as f32 * 0.01_f32).cos()
* 0.5 + 0.5) * 1024.0_f32;
let tx = ((uv_x as i32).rem_euclid(1024)) as usize;
let ty = ((uv_y as i32).rem_euclid(1024)) as usize;
tex_cache[y * TILE_SIZE + x] = texture[ty * 1024 + tx];
}
}
for ty in 0..TILE_SIZE {
let py = (tile_y * TILE_SIZE + ty) as u32;
for tx in 0..TILE_SIZE {
let px = (tile_x * TILE_SIZE + tx) as u32;
fragment_shader(px, py, frame, &tex_cache, depth_buf, color_buf);
}
}
}
#[inline(never)]
fn render_frame_tiled(frame: u32, texture: &[u32], color_buf: &mut [u32], depth_buf: &mut [f32]) {
let tiles_x = (WIDTH + TILE_SIZE - 1) / TILE_SIZE;
let tiles_y = (HEIGHT + TILE_SIZE - 1) / TILE_SIZE;
for ty in 0..tiles_y {
for tx in 0..tiles_x {
render_tile(tx, ty, frame, texture, color_buf, depth_buf);
}
}
}
fn main() {
println!("Aetheric Silicon — TILE-BATCHED Software Rasterizer");
println!("Tiles: {}x{}", WIDTH / TILE_SIZE, HEIGHT / TILE_SIZE);
println!("Tile size: {0}x{0} pixels ({0}² = {1} pixels/tile)", TILE_SIZE, TILE_SIZE * TILE_SIZE);
println!("Total tiles: {}\n", (WIDTH / TILE_SIZE) * (HEIGHT / TILE_SIZE));
let texture = build_texture();
let mut color_buf = vec![0xFF101018u32; WIDTH * HEIGHT];
let mut depth_buf = vec![1.0_f32; WIDTH * HEIGHT];
println!("Texture: 4 MiB");
println!("Color/Deph buffers: 3.5 MiB each\n");
for f in 0..3 {
render_frame_tiled(f, &texture, &mut color_buf, &mut depth_buf);
}
let n_frames = 30;
let start = Instant::now();
let mut checksum: u64 = 0;
for f in 3..(3 + n_frames) {
render_frame_tiled(f, &texture, &mut color_buf, &mut depth_buf);
checksum = checksum.wrapping_add(color_buf[((f as usize) * WIDTH + 7) % color_buf.len()] as u64);
checksum = checksum.wrapping_add(depth_buf[((f as usize) * WIDTH + 11) % depth_buf.len()].to_bits() as u64);
}
let elapsed = start.elapsed();
println!("checksum: 0x{:016x}", checksum);
let ms_per_frame = elapsed.as_secs_f64() * 1000.0 / n_frames as f64;
let fps = n_frames as f64 / elapsed.as_secs_f64();
let mpix_s = (WIDTH * HEIGHT) as f64 * fps / 1e6;
println!("\n--- RESULTS ---");
println!("Time / frame: {:.2} ms ({:.1} fps)", ms_per_frame, fps);
println!("Pixels / sec: {:.1} Mpixels/s", mpix_s);
println!("Total bench time: {:.2} seconds", elapsed.as_secs_f64());
println!("\nCompare: simple rasterizer (no tile batching) = 0.6 ms/frame; ");
println!(" heavy = 360 ms/frame");
println!("\n--- COMPARISON to dGPU ---");
println!("dGPU at 720p heavy shaders: 60-150 fps (55-138 Mpixels/s)");
println!("Our ratio: {:.2}x", mpix_s / 120.0);
if (mpix_s / 120.0) >= 0.625 {
println!("[v] INSIDE 1.6x envelope of dGPU!");
} else {
println!("[x] still outside 1.6x envelope (need {:.1} Mpixels/s for that, have {:.1})",
120.0 * 0.625, mpix_s);
}
}