Skip to content

Commit

Permalink
feat: single and multithreaded executors
Browse files Browse the repository at this point in the history
  • Loading branch information
Stumblinbear committed Jan 21, 2024
1 parent 3b51fd7 commit 2eac62b
Show file tree
Hide file tree
Showing 12 changed files with 538 additions and 190 deletions.
1 change: 0 additions & 1 deletion crates/agui_core/src/element/comparison.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// TODO: ElementComparison
#[derive(Debug, PartialEq, Eq)]
pub enum ElementComparison {
/// The widgets were of the same type and instance.
Expand Down
2 changes: 1 addition & 1 deletion crates/agui_core/src/element/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ use crate::{element::render::ElementRender, render::view::View};

pub trait ElementView: ElementRender {
/// Creates a the view for this subtree.
fn create_view(&self) -> Box<dyn View>;
fn create_view(&self) -> Box<dyn View + Send>;
}
184 changes: 1 addition & 183 deletions crates/agui_core/src/engine/mod.rs
Original file line number Diff line number Diff line change
@@ -1,189 +1,7 @@
use std::{
future::Future,
pin::Pin,
sync::mpsc,
time::{Duration, Instant},
};

use futures::{
executor::{LocalPool, LocalSpawner},
future::RemoteHandle,
task::{LocalSpawnExt, SpawnError, SpawnExt},
};

use crate::{
element::ElementId,
engine::{
bindings::{ElementBinding, SchedulerBinding},
render_manager::RenderManager,
widgets::WidgetManager,
},
widget::Widget,
};

pub mod bindings;
mod dirty;
pub mod render_manager;
pub mod rendering;
pub mod update_notifier;
pub mod widgets;

pub use dirty::Dirty;

pub struct Engine {
widget_manager: WidgetManager<EngineElementBinding, EngineSchedulerBinding>,
render_manager: RenderManager,

spawned_rx: mpsc::Receiver<ElementId>,
rebuilt_rx: mpsc::Receiver<ElementId>,
forget_rx: mpsc::Receiver<ElementId>,

pool: LocalPool,
}

impl Engine {
pub fn with_root(root: Widget) -> Engine {
let (spawned_tx, spawned_rx) = mpsc::channel();
let (rebuilt_tx, rebuilt_rx) = mpsc::channel();
let (forget_tx, forget_rx) = mpsc::channel();

let pool = LocalPool::default();

Engine {
widget_manager: WidgetManager::builder()
.with_root(root)
.with_element_binding(EngineElementBinding {
spawned_tx,
rebuilt_tx,
forget_tx,
})
.with_scheduler(EngineSchedulerBinding {
spawner: pool.spawner(),
})
.build(),
render_manager: RenderManager::default(),

spawned_rx,
rebuilt_rx,
forget_rx,

pool,
}
}

pub fn run(mut self) {
loop {
let timings = self.update();

tracing::debug!(?timings, "update complete");

self.pool.run_until(self.widget_manager.wait_for_update());
}
}

fn update(&mut self) -> UpdateTimings {
let start = Instant::now();

self.widget_manager.update();

let update_widget_tree_end = Instant::now();

for element_id in self.forget_rx.try_iter() {
self.render_manager.forget_element(element_id)
}

for element_id in self.spawned_rx.try_iter() {
self.render_manager.on_create_element(element_id)
}

for element_id in self.rebuilt_rx.try_iter() {
self.render_manager.on_needs_update(element_id)
}

self.render_manager
.sync_render_objects(self.widget_manager.tree());

let update_render_tree_end = Instant::now();

// TODO: maybe do this async in case an update triggers while we're
// performaing layout and painting?
self.render_manager.flush_layout();

let layout_end = Instant::now();

self.render_manager.flush_paint();

let paint_end = Instant::now();

self.render_manager.sync_views();

let sync_views_end = Instant::now();

UpdateTimings {
duration: start.elapsed(),

update_widget_tree: update_widget_tree_end - start,
update_render_tree: update_render_tree_end - update_widget_tree_end,
layout: layout_end - update_render_tree_end,
paint: paint_end - layout_end,
sync_views: sync_views_end - paint_end,
}
}
}

struct EngineElementBinding {
spawned_tx: mpsc::Sender<ElementId>,
rebuilt_tx: mpsc::Sender<ElementId>,
forget_tx: mpsc::Sender<ElementId>,
}

impl ElementBinding for EngineElementBinding {
fn on_element_spawned(&mut self, _: Option<ElementId>, id: ElementId) {
self.spawned_tx.send(id).ok();
}

fn on_element_needs_rebuild(&mut self, id: ElementId) {
self.rebuilt_tx.send(id).ok();
}

fn on_element_destroyed(&mut self, id: ElementId) {
self.forget_tx.send(id).ok();
}
}

struct EngineSchedulerBinding {
spawner: LocalSpawner,
}

impl SchedulerBinding for EngineSchedulerBinding {
fn spawn_local_task(
&mut self,
id: ElementId,
future: Pin<Box<dyn Future<Output = ()> + 'static>>,
) -> Result<RemoteHandle<()>, SpawnError> {
tracing::trace!("spawning local task for {:?}", id);

self.spawner.spawn_local_with_handle(future)
}

fn spawn_shared_task(
&mut self,
id: ElementId,
future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
) -> Result<RemoteHandle<()>, SpawnError> {
tracing::trace!("spawning shared task for {:?}", id);

self.spawner.spawn_with_handle(future)
}
}

#[derive(Debug)]
#[allow(dead_code)]
struct UpdateTimings {
duration: Duration,

update_widget_tree: Duration,
update_render_tree: Duration,
layout: Duration,
paint: Duration,
sync_views: Duration,
}
File renamed without changes.
Loading

0 comments on commit 2eac62b

Please sign in to comment.