use crate::graphics;
use crate::Result;
pub struct Task<T> {
total_work: u32,
function: Box<dyn FnOnce(&mut Worker<'_>) -> Result<T>>,
}
impl<T> Task<T> {
pub fn new<F>(f: F) -> Task<T>
where
F: 'static + FnOnce() -> Result<T>,
{
Task {
total_work: 1,
function: Box::new(move |worker| {
let result = f();
worker.notify_progress(1);
result
}),
}
}
pub fn succeed<F>(f: F) -> Task<T>
where
F: 'static + FnOnce() -> T,
{
Task::new(move || Ok(f()))
}
pub fn using_gpu<F>(f: F) -> Task<T>
where
F: 'static + FnOnce(&mut graphics::Gpu) -> Result<T>,
{
Task::sequence(1, move |worker| {
let result = f(worker.gpu());
worker.notify_progress(1);
result
})
}
pub(crate) fn sequence<F>(total_work: u32, f: F) -> Task<T>
where
F: 'static + FnOnce(&mut Worker<'_>) -> Result<T>,
{
Task {
total_work,
function: Box::new(f),
}
}
pub fn stage<S: Into<String>>(title: S, task: Task<T>) -> Task<T>
where
T: 'static,
{
let title = title.into();
Task {
total_work: task.total_work,
function: Box::new(move |worker| {
worker.with_stage(title.clone(), task.function)
}),
}
}
pub fn total_work(&self) -> u32 {
self.total_work
}
pub fn map<F, A>(self, f: F) -> Task<A>
where
T: 'static,
F: 'static + FnOnce(T) -> A,
{
Task {
total_work: self.total_work,
function: Box::new(move |worker| match (self.function)(worker) {
Ok(value) => Ok(f(value)),
Err(error) => Err(error),
}),
}
}
pub fn run(self, gpu: &mut graphics::Gpu) -> Result<T> {
let mut worker = Worker::Headless(gpu);
(self.function)(&mut worker)
}
pub(crate) fn run_with_window<F>(
self,
window: &mut graphics::Window,
mut on_progress: F,
) -> Result<T>
where
F: FnMut(&Progress, &mut graphics::Window) -> (),
{
let mut worker = Worker::Windowed {
window,
listener: &mut on_progress,
progress: Progress {
total_work: self.total_work,
work_completed: 0,
stages: Vec::new(),
},
};
worker.notify_progress(0);
(self.function)(&mut worker)
}
}
impl<T> std::fmt::Debug for Task<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Task {{ total_work: {} }}", self.total_work)
}
}
pub(crate) enum Worker<'a> {
Headless(&'a mut graphics::Gpu),
Windowed {
window: &'a mut graphics::Window,
listener: &'a mut dyn FnMut(&Progress, &mut graphics::Window) -> (),
progress: Progress,
},
}
impl<'a> Worker<'a> {
pub fn gpu(&mut self) -> &mut graphics::Gpu {
match self {
Worker::Headless(gpu) => gpu,
Worker::Windowed { window, .. } => window.gpu(),
}
}
pub fn notify_progress(&mut self, work: u32) {
match self {
Worker::Headless(_) => {}
Worker::Windowed {
progress,
window,
listener,
..
} => {
progress.work_completed += work;
listener(&progress, window);
}
};
}
pub fn with_stage<T>(
&mut self,
title: String,
f: Box<dyn FnOnce(&mut Worker<'_>) -> T>,
) -> T {
match self {
Worker::Headless(_) => f(self),
Worker::Windowed { .. } => {
if let Worker::Windowed { progress, .. } = self {
progress.stages.push(title);
}
self.notify_progress(0);
let result = f(self);
if let Worker::Windowed { progress, .. } = self {
let _ = progress.stages.pop();
}
result
}
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct Progress {
total_work: u32,
work_completed: u32,
stages: Vec<String>,
}
impl Progress {
pub fn total_work(&self) -> u32 {
self.total_work
}
pub fn completed_work(&self) -> u32 {
self.work_completed.min(self.total_work)
}
pub fn percentage(&self) -> f32 {
self.completed_work() as f32 / self.total_work.max(1) as f32 * 100.0
}
pub fn stage(&self) -> Option<&String> {
self.stages.last()
}
}
pub trait Join {
type Type;
fn join(self) -> Task<Self::Type>;
}
impl<A: 'static, B: 'static> Join for (Task<A>, Task<B>) {
type Type = (A, B);
fn join(self) -> Task<(A, B)> {
let (loader_a, loader_b) = self;
Task::sequence(
loader_a.total_work() + loader_b.total_work(),
move |task| {
(loader_a.function)(task)
.and_then(|a| (loader_b.function)(task).map(|b| (a, b)))
},
)
}
}
impl<A: 'static, B: 'static, C: 'static> Join for (Task<A>, Task<B>, Task<C>) {
type Type = (A, B, C);
fn join(self) -> Task<(A, B, C)> {
let (loader_a, loader_b, loader_c) = self;
((loader_a, loader_b).join(), loader_c)
.join()
.map(|((a, b), c)| (a, b, c))
}
}
impl<A: 'static, B: 'static, C: 'static, D: 'static> Join
for (Task<A>, Task<B>, Task<C>, Task<D>)
{
type Type = (A, B, C, D);
fn join(self) -> Task<(A, B, C, D)> {
let (loader_a, loader_b, loader_c, loader_d) = self;
((loader_a, loader_b, loader_c).join(), loader_d)
.join()
.map(|((a, b, c), d)| (a, b, c, d))
}
}
impl<A: 'static, B: 'static, C: 'static, D: 'static, E: 'static> Join
for (Task<A>, Task<B>, Task<C>, Task<D>, Task<E>)
{
type Type = (A, B, C, D, E);
fn join(self) -> Task<(A, B, C, D, E)> {
let (loader_a, loader_b, loader_c, loader_d, loader_e) = self;
((loader_a, loader_b, loader_c, loader_d).join(), loader_e)
.join()
.map(|((a, b, c, d), e)| (a, b, c, d, e))
}
}
impl<
A: 'static,
B: 'static,
C: 'static,
D: 'static,
E: 'static,
F: 'static,
> Join for (Task<A>, Task<B>, Task<C>, Task<D>, Task<E>, Task<F>)
{
type Type = (A, B, C, D, E, F);
fn join(self) -> Task<(A, B, C, D, E, F)> {
let (loader_a, loader_b, loader_c, loader_d, loader_e, loader_f) = self;
(
(loader_a, loader_b, loader_c, loader_d, loader_e).join(),
loader_f,
)
.join()
.map(|((a, b, c, d, e), f)| (a, b, c, d, e, f))
}
}
impl<
A: 'static,
B: 'static,
C: 'static,
D: 'static,
E: 'static,
F: 'static,
G: 'static,
> Join
for (
Task<A>,
Task<B>,
Task<C>,
Task<D>,
Task<E>,
Task<F>,
Task<G>,
)
{
type Type = (A, B, C, D, E, F, G);
fn join(self) -> Task<(A, B, C, D, E, F, G)> {
let (
loader_a,
loader_b,
loader_c,
loader_d,
loader_e,
loader_f,
loader_g,
) = self;
(
(loader_a, loader_b, loader_c, loader_d, loader_e, loader_f).join(),
loader_g,
)
.join()
.map(|((a, b, c, d, e, f), g)| (a, b, c, d, e, f, g))
}
}
impl<
A: 'static,
B: 'static,
C: 'static,
D: 'static,
E: 'static,
F: 'static,
G: 'static,
H: 'static,
> Join
for (
Task<A>,
Task<B>,
Task<C>,
Task<D>,
Task<E>,
Task<F>,
Task<G>,
Task<H>,
)
{
type Type = (A, B, C, D, E, F, G, H);
fn join(self) -> Task<(A, B, C, D, E, F, G, H)> {
let (
loader_a,
loader_b,
loader_c,
loader_d,
loader_e,
loader_f,
loader_g,
loader_h,
) = self;
(
(
loader_a, loader_b, loader_c, loader_d, loader_e, loader_f,
loader_g,
)
.join(),
loader_h,
)
.join()
.map(|((a, b, c, d, e, f, g), h)| (a, b, c, d, e, f, g, h))
}
}