Engine structure
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
use std::{io::stdout, sync::Arc, time::Instant};
|
||||
|
||||
use crossterm::{cursor::MoveTo, execute, terminal::Clear};
|
||||
|
||||
use crate::{prelude::Context, render::Area, DrawContext, View};
|
||||
|
||||
use super::Engine;
|
||||
|
||||
pub struct Benchmark<T: Engine>(T);
|
||||
|
||||
impl<T: Engine> Benchmark<T> {
|
||||
pub fn new(engine: T) -> Self {
|
||||
Self(engine)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Engine> Engine for Benchmark<T> {
|
||||
fn run<F: Fn(&Arc<Context>) -> View + Send + Sync + 'static>(&self, component: F) {
|
||||
let mut times: Vec<usize> = Vec::new();
|
||||
let cx = self.init(component);
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
for _ in 0..100 {
|
||||
let start = Instant::now();
|
||||
self.render(&cx);
|
||||
let end = Instant::now();
|
||||
times.push(end.duration_since(start).as_millis() as usize);
|
||||
self.0.render_delay();
|
||||
}
|
||||
|
||||
let end = Instant::now();
|
||||
|
||||
execute!(stdout(), Clear(crossterm::terminal::ClearType::Purge)).unwrap();
|
||||
execute!(stdout(), Clear(crossterm::terminal::ClearType::All)).unwrap();
|
||||
execute!(stdout(), MoveTo(0, 0)).unwrap();
|
||||
|
||||
println!(
|
||||
"Average time: {}ms",
|
||||
if times.len() > 0 {
|
||||
times.iter().sum::<usize>() / times.len()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
);
|
||||
println!("Min time: {}ms", times.iter().min().unwrap_or(&0));
|
||||
println!("Max time: {}ms", times.iter().max().unwrap_or(&0));
|
||||
println!(
|
||||
"Median time: {}ms",
|
||||
times.iter().nth(times.len() / 2).unwrap_or(&0)
|
||||
);
|
||||
println!(
|
||||
"90th percentile: {}ms",
|
||||
times.iter().nth(times.len() * 90 / 100).unwrap_or(&0)
|
||||
);
|
||||
println!(
|
||||
"95th percentile: {}ms",
|
||||
times.iter().nth(times.len() * 95 / 100).unwrap_or(&0)
|
||||
);
|
||||
println!(
|
||||
"99th percentile: {}ms",
|
||||
times.iter().nth(times.len() * 99 / 100).unwrap_or(&0)
|
||||
);
|
||||
|
||||
println!("Total time: {}ms", end.duration_since(start).as_millis());
|
||||
}
|
||||
|
||||
fn init<F: Fn(&Arc<Context>) -> View + Send + Sync + 'static>(
|
||||
&self,
|
||||
component: F,
|
||||
) -> Arc<Context> {
|
||||
self.0.init(component)
|
||||
}
|
||||
|
||||
fn draw_context(&self, ctx: &DrawContext) {
|
||||
self.0.draw_context(ctx)
|
||||
}
|
||||
|
||||
fn render(&self, cx: &Arc<Context>) {
|
||||
self.0.render(cx)
|
||||
}
|
||||
|
||||
fn render_view(&self, area: &Area, view: &View) -> DrawContext {
|
||||
self.0.render_view(area, view)
|
||||
}
|
||||
|
||||
fn render_delay(&self) {
|
||||
self.0.render_delay();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
use std::{
|
||||
io::{stdout, Write},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use crossterm::{cursor::MoveTo, execute, terminal::Clear};
|
||||
|
||||
use crate::{prelude::Context, render::Area, DrawContext, View};
|
||||
|
||||
use super::Engine;
|
||||
|
||||
pub struct Console {
|
||||
threads: Mutex<Vec<Arc<dyn Fn(Arc<Context>) + Send + Sync>>>,
|
||||
}
|
||||
|
||||
impl Console {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
threads: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread<F: Fn(Arc<Context>) + Send + Sync + 'static>(&self, run: F) {
|
||||
self.threads.lock().unwrap().push(Arc::new(run));
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for Console {
|
||||
fn render_view(&self, area: &Area, view: &View) -> DrawContext {
|
||||
let mut context = DrawContext::new(area.clone());
|
||||
view(&mut context);
|
||||
context
|
||||
}
|
||||
|
||||
fn draw_context(&self, ctx: &DrawContext) {
|
||||
for inst in &ctx.drawing {
|
||||
match inst {
|
||||
crate::render::DrawInstruction::Text(point, text) => {
|
||||
let (x, y) = (ctx.area.x + point.x, ctx.area.y + point.y);
|
||||
execute!(stdout(), MoveTo(x, y),).unwrap();
|
||||
print!("{text}");
|
||||
stdout().flush().unwrap();
|
||||
}
|
||||
crate::render::DrawInstruction::Child(_point, child) => self.draw_context(child),
|
||||
crate::render::DrawInstruction::View(area, view) => {
|
||||
self.draw_context(&self.render_view(area, view))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, cx: &Arc<Context>) {
|
||||
let (width, height) = crossterm::terminal::size().unwrap();
|
||||
|
||||
execute!(stdout(), Clear(crossterm::terminal::ClearType::Purge)).unwrap();
|
||||
execute!(stdout(), Clear(crossterm::terminal::ClearType::All)).unwrap();
|
||||
|
||||
self.draw_context(&self.render_view(
|
||||
&Area {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
},
|
||||
&cx.get_view(),
|
||||
));
|
||||
}
|
||||
|
||||
fn init<F: Fn(&Arc<Context>) -> View + Send + Sync + 'static>(
|
||||
&self,
|
||||
component: F,
|
||||
) -> Arc<Context> {
|
||||
let cx = Context::new(component);
|
||||
cx.refresh();
|
||||
|
||||
for thread in self.threads.lock().unwrap().iter() {
|
||||
let thread = thread.clone();
|
||||
|
||||
std::thread::spawn({
|
||||
let cx = cx.clone();
|
||||
move || thread(cx)
|
||||
});
|
||||
}
|
||||
|
||||
cx
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
pub mod benchmark;
|
||||
pub mod console;
|
||||
|
||||
pub use benchmark::*;
|
||||
pub use console::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{prelude::Context, render::Area, DrawContext, View};
|
||||
|
||||
pub trait Engine {
|
||||
fn run<F: Fn(&Arc<Context>) -> View + Send + Sync + 'static>(&self, component: F) {
|
||||
let cx = self.init(component);
|
||||
|
||||
loop {
|
||||
self.render(&cx);
|
||||
self.render_delay();
|
||||
}
|
||||
}
|
||||
|
||||
fn init<F: Fn(&Arc<Context>) -> View + Send + Sync + 'static>(
|
||||
&self,
|
||||
component: F,
|
||||
) -> Arc<Context>;
|
||||
fn render(&self, cx: &Arc<Context>);
|
||||
fn render_delay(&self) {
|
||||
std::thread::sleep(std::time::Duration::from_millis(16))
|
||||
}
|
||||
|
||||
fn render_view(&self, area: &Area, view: &View) -> DrawContext;
|
||||
fn draw_context(&self, ctx: &DrawContext);
|
||||
}
|
||||
Reference in New Issue
Block a user