M src/application.rs => src/application.rs +11 -2
@@ 1,4 1,6 @@
use sdl2::event::Event;
+use std::time::{Duration, Instant};
+use std::thread;
use crate::engine::Engine;
use crate::error::*;
@@ 6,6 8,8 @@ use crate::input_context::InputContext;
use crate::sdl::SDLContext;
use crate::window::Window;
+const FRAME_INTERVAL: Duration = Duration::new(0, 1_000_000_000 / 60); // 60Hz
+
pub struct Application {
pub engine: Engine,
}
@@ 22,6 26,8 @@ impl Application {
let mut event_pump = sdl_ctx.sdl.event_pump()?;
'main: loop {
+ let next_frame_time = Instant::now() + FRAME_INTERVAL;
+
let mut input_ctx = InputContext::new();
input_ctx.set_touch_plane_size(window.render_ctx.viewport_size);
@@ 36,8 42,11 @@ impl Application {
render_ctx.clear();
self.engine.frame(render_ctx, &mut input_ctx);
-
- window.swap(); /* will block until next vsync */
+ window.swap();
+
+ // sleep until next frame
+ let sleep_duration = next_frame_time.saturating_duration_since(Instant::now());
+ thread::sleep(sleep_duration);
}
Ok(())
A src/components/fps.rs => src/components/fps.rs +61 -0
@@ 0,0 1,61 @@
+use crate::components::{
+ quad::QuadComponent,
+ text::TextComponent,
+};
+use crate::geometry::Rect;
+use crate::input_context::InputContext;
+use crate::render_context::RenderContext;
+use crate::scene::Scene;
+
+use std::time::Instant;
+
+pub struct FPSComponent {
+ fps: usize,
+ frames: usize,
+ last_latch_time: Option<Instant>,
+}
+
+impl FPSComponent {
+ pub fn new() -> Self {
+ Self {
+ fps: 0,
+ frames: 0,
+ last_latch_time: None,
+ }
+ }
+}
+
+pub fn fps_system(scene: &mut Scene, _: &mut RenderContext, _: &mut InputContext) {
+ for entity in &mut scene.entities {
+ let fps = match &mut entity.fps { None => continue, Some(c) => c };
+
+ // initialize text component
+ if entity.text.is_none() {
+ entity.text = Some(TextComponent::new("FPS: 0"));
+ }
+
+ // initialize quad component
+ if entity.quad.is_none() {
+ entity.quad = Some(QuadComponent::new(&Rect::default()));
+ }
+
+ // calculate FPS and update text
+ fps.frames += 1;
+ match fps.last_latch_time {
+ Some(last_latch_time) => {
+ if last_latch_time.elapsed().as_secs() >= 1 {
+ fps.fps = fps.frames;
+ fps.frames = 0;
+ fps.last_latch_time = Some(Instant::now());
+ }
+ },
+
+ None => {
+ fps.last_latch_time = Some(Instant::now());
+ }
+ }
+
+ let text = &mut entity.text.as_mut().unwrap();
+ text.set_text(&format!("FPS: {:}", fps.fps));
+ }
+}
M src/components/mod.rs => src/components/mod.rs +1 -0
@@ 1,5 1,6 @@
pub mod camera;
pub mod clock;
+pub mod fps;
pub mod material;
pub mod path;
pub mod quad;
M src/engine.rs => src/engine.rs +2 -0
@@ 4,6 4,7 @@ use crate::scene::Scene;
use crate::components::{
camera::camera_system,
clock::clock_system,
+ fps::fps_system,
material::material_system,
path::path_system,
quad::quad_system,
@@ 37,6 38,7 @@ impl Engine {
// ordering matters a little less here.
Box::new(clock_system),
+ Box::new(fps_system),
],
}
}
M src/entity.rs => src/entity.rs +3 -0
@@ 7,6 7,7 @@ use ultraviolet::{Mat4, Vec3};
use crate::components::{
camera::CameraComponent,
clock::ClockComponent,
+ fps::FPSComponent,
material::{Material, MaterialComponent},
path::PathComponent,
quad::QuadComponent,
@@ 37,6 38,7 @@ pub struct Entity {
/* auxiliary components */
pub clock: Option<ClockComponent>,
+ pub fps: Option<FPSComponent>,
event_handler: Option<Rc<EventHandler>>,
}
@@ 56,6 58,7 @@ impl Entity {
path: None,
clock: None,
+ fps: None,
event_handler: None,
}
M src/main.rs => src/main.rs +8 -1
@@ 19,11 19,11 @@ mod util;
mod vertex_array;
mod window;
-
use crate::{
components::{
camera::CameraComponent,
clock::ClockComponent,
+ fps::FPSComponent,
material::MaterialComponent,
touch::TouchComponent,
transform::TransformComponent,
@@ 125,6 125,13 @@ fn main() {
let square = scene.find_entity_mut(square_id).unwrap();
square.transform.as_mut().unwrap().rotation = Rotor3::from_rotation_xy(theta);
});
+
+ // FPS counter
+ let mut fps = Entity::new();
+ fps.transform = Some(TransformComponent::new());
+ fps.material = Some(MaterialComponent::with_color(&Color::WHITE));
+ fps.fps = Some(FPSComponent::new());
+ scene.add_entity(fps);
// run the scene
app.engine.scene = Some(scene);