summaryrefslogtreecommitdiff
path: root/alligator_render/examples/bunnymark.rs
blob: 4fe37d38ae5cf1f7479ca254d8a002063883029c (plain)
use std::{num::NonZeroU32, time::Instant};

use alligator_render::{
	ImageFormat, Instance, InstanceId, RenderWindowConfig, Renderer, TextureId,
};

fn xorshift_plus(seed: &mut [u64; 2]) -> u64 {
	let mut t = seed[0];
	let s = seed[1];

	t ^= t << 23;
	t ^= t >> 18;
	t ^= s ^ (s >> 5);

	seed[0] = s;
	seed[1] = t;

	t + s
}

#[derive(Debug)]
struct State {
	texture_id: TextureId,
	bunnies: Vec<Bunny>,
	previous_timestamp: Option<Instant>,
	seed: [u64; 2],
	stopped: bool,
}

impl State {
	fn new(texture_id: TextureId) -> Self {
		Self {
			texture_id,
			bunnies: Vec::with_capacity(10_000_000),
			previous_timestamp: None,
			seed: [0x0D15EA5E8BADF00D, 0xDECAFBADDEADBEAF],
			stopped: false,
		}
	}

	#[profiling::function]
	fn update(&mut self, renderer: &mut Renderer) {
		let Some(instant) = self.previous_timestamp else {
			self.previous_timestamp = Some(Instant::now());
			return;
		};

		let frame_time = instant.elapsed();
		let fps = 1.0 / frame_time.as_secs_f32();

		renderer.set_title(&format!(
			"BunnyMark - {} bunnies - {} FPS",
			self.bunnies.len(),
			fps.round()
		));

		if fps < 15.0 {
			self.stopped = true;
		}

		self.previous_timestamp = Some(Instant::now());

		if self.stopped {
			return;
		}

		for bunny in self.bunnies.iter_mut() {
			let instance = renderer
				.instances_mut()
				.get_instance_mut(bunny.instance_id)
				.unwrap();

			instance.position[0] += bunny.velocity_x;
			instance.position[1] += bunny.velocity_y;

			if !(-1.5..1.5).contains(&instance.position[0]) {
				instance.position[0] = instance.position[0].clamp(-1.0, 1.0);
				bunny.velocity_x = -bunny.velocity_x;
			}

			if !(-0.75..0.75).contains(&instance.position[1]) {
				instance.position[1] = instance.position[1].clamp(-0.5, 0.5);
				bunny.velocity_y *= -0.90;
			}

			bunny.velocity_y -= 0.005;
		}

		for _ in 0..=(fps as u64 * 50) {
			let texture_x = renderer.textures().texture_x(self.texture_id).unwrap();
			let texture_y = renderer.textures().texture_x(self.texture_id).unwrap();
			let texture_height = renderer.textures().texture_height(self.texture_id).unwrap();
			let texture_width = renderer.textures().texture_width(self.texture_id).unwrap();
			let instance_id = renderer.instances_mut().push_instance(Instance {
				texture_coordinates: [texture_x, texture_y],
				texture_size: [texture_width, texture_height],
				size: [0.08, 0.08],
				position: [-1.5, 0.70],
				..Default::default()
			});

			let velocity_x = (xorshift_plus(&mut self.seed) % 1_000_000) as f32 / 25_000_000.0;
			let velocity_y = (xorshift_plus(&mut self.seed) % 1_000_000) as f32 / 25_000_000.0;
			self.bunnies.push(Bunny {
				instance_id,
				velocity_x,
				velocity_y,
			});
		}
	}
}

#[derive(Debug, Clone, Copy)]
struct Bunny {
	instance_id: InstanceId,
	velocity_x: f32,
	velocity_y: f32,
}

fn main() {
	#[cfg(feature = "profile-with-tracy")]
	profiling::tracy_client::Client::start();
	profiling::register_thread!("main");

	// configure the render window
	let config = RenderWindowConfig {
		title: "BunnyMark",
		instance_capacity: 10_000_000,
		default_width: NonZeroU32::new(1280).unwrap(),
		default_height: NonZeroU32::new(720).unwrap(),
		vsync: false,
		low_power: false,
		..Default::default()
	};

	let bunny = include_bytes!("res/bunny.ff");
	let mut renderer = Renderer::new(&config).unwrap();
	let texture_id = renderer
		.textures_mut()
		.load_from_memory(bunny, ImageFormat::Farbfeld)
		.unwrap();

	let state = Box::leak(Box::new(State::new(texture_id)));
	renderer.run(|r| state.update(r));
}