extern crate gfx;
extern crate specs;
use std::{thread, time};
use std::sync::mpsc;
pub type Delta = f32;
pub type Planner = specs::Planner<Delta>;
pub const DRAW_PRIORITY: specs::Priority = 10;
pub const DRAW_NAME: &'static str = "draw";
pub trait Init: 'static {
type Shell: 'static + Send;
fn start(self, &mut Planner) -> Self::Shell;
fn proceed(_: &mut Self::Shell, _: &specs::World) -> bool { true }
}
struct App<I: Init> {
shell: I::Shell,
planner: Planner,
last_time: time::Instant,
}
impl<I: Init> App<I> {
fn tick(&mut self) -> bool {
let elapsed = self.last_time.elapsed();
self.last_time = time::Instant::now();
let delta = elapsed.subsec_nanos() as f32 / 1e9 + elapsed.as_secs() as f32;
self.planner.dispatch(delta);
I::proceed(&mut self.shell, self.planner.mut_world())
}
}
struct ChannelPair<R: gfx::Resources, C: gfx::CommandBuffer<R>> {
receiver: mpsc::Receiver<gfx::Encoder<R, C>>,
sender: mpsc::Sender<gfx::Encoder<R, C>>,
}
pub trait Painter<R: gfx::Resources>: 'static + Send {
type Visual: specs::Component;
fn draw<'a, I, C>(&mut self, I, &mut gfx::Encoder<R, C>) where
I: Iterator<Item = &'a Self::Visual>,
C: gfx::CommandBuffer<R>;
}
struct DrawSystem<R: gfx::Resources, C: gfx::CommandBuffer<R>, P> {
painter: P,
channel: ChannelPair<R, C>,
}
impl<R, C, P> specs::System<Delta> for DrawSystem<R, C, P>
where
R: 'static + gfx::Resources,
C: 'static + Send + gfx::CommandBuffer<R>,
P: Painter<R>,
{
fn run(&mut self, arg: specs::RunArg, _: Delta) {
use specs::Join;
let mut encoder = match self.channel.receiver.recv() {
Ok(r) => r,
Err(_) => return,
};
let vis = arg.fetch(|w| w.read::<P::Visual>());
self.painter.draw((&vis).iter(), &mut encoder);
let _ = self.channel.sender.send(encoder);
}
}
pub struct Pegasus<D: gfx::Device> {
pub device: D,
channel: ChannelPair<D::Resources, D::CommandBuffer>,
_guard: thread::JoinHandle<()>,
}
pub struct Swing<'a, D: 'a + gfx::Device> {
device: &'a mut D,
}
impl<'a, D: 'a + gfx::Device> Drop for Swing<'a, D> {
fn drop(&mut self) {
self.device.cleanup();
}
}
impl<D: gfx::Device> Pegasus<D> {
pub fn new<F, I, P>(init: I, device: D, painter: P, mut com_factory: F)
-> Pegasus<D> where
I: Init,
D::CommandBuffer: 'static + Send, P: Painter<D::Resources>,
F: FnMut() -> D::CommandBuffer,
{
let (app_send, dev_recv) = mpsc::channel();
let (dev_send, app_recv) = mpsc::channel();
for _ in 0..2 {
let enc = gfx::Encoder::from(com_factory());
app_send.send(enc).unwrap();
}
let mut app = {
let draw_sys = DrawSystem {
painter: painter,
channel: ChannelPair {
receiver: app_recv,
sender: app_send,
},
};
let mut w = specs::World::new();
w.register::<P::Visual>();
let mut plan = specs::Planner::new(w, 4);
plan.add_system(draw_sys, DRAW_NAME, DRAW_PRIORITY);
let shell = init.start(&mut plan);
App::<I> {
shell: shell,
planner: plan,
last_time: time::Instant::now(),
}
};
Pegasus {
device: device,
channel: ChannelPair {
sender: dev_send,
receiver: dev_recv,
},
_guard: thread::spawn(move || {
while app.tick() {}
}),
}
}
pub fn swing(&mut self) -> Option<Swing<D>> {
match self.channel.receiver.recv() {
Ok(mut encoder) => {
encoder.flush(&mut self.device);
if self.channel.sender.send(encoder).is_err() {
return None
}
Some(Swing {
device: &mut self.device,
})
},
Err(_) => None,
}
}
}