wip: better desktop support

This commit is contained in:
Jonathan Kelley committed 2021-11-16 01:25:38 -05:00
1 parent 19df1bda10
commit 25a8411485
11 files changed
+519 -586

No files matched your search

+22 -21
View File
@@ -643,9 +643,9 @@ impl<'a> NodeFactory<'a> {
}
}
pub fn fragment_from_iter(
pub fn fragment_from_iter<'b, 'c>(
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
) -> VNode<'a> {
let bump = self.bump;
let mut nodes = bumpalo::collections::Vec::new_in(bump);
@@ -662,24 +662,25 @@ impl<'a> NodeFactory<'a> {
let children = nodes.into_bump_slice();
// TODO
// We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
//
// if cfg!(debug_assertions) {
// if children.len() > 1 {
// if children.last().unwrap().key().is_none() {
// log::error!(
// r#"
// Warning: Each child in an array or iterator should have a unique "key" prop.
// Not providing a key will lead to poor performance with lists.
// See docs.rs/dioxus for more information.
// ---
// To help you identify where this error is coming from, we've generated a backtrace.
// "#,
// );
// }
// }
// }
// todo: add a backtrace
if cfg!(debug_assertions) && children.len() > 1 && children.last().unwrap().key().is_none()
{
// todo: rsx! calls get turned into fragments which means they always trip this error
//
//
// use backtrace::Backtrace;
// let bt = Backtrace::new();
log::error!(
r#"
Warning: Each child in an array or iterator should have a unique "key" prop.
Not providing a key will lead to poor performance with lists.
See docs.rs/dioxus for more information.
-------------
"#,
// bt
);
}
VNode::Fragment(VFragment {
children,
@@ -820,7 +821,7 @@ impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
}
}
impl<'a> IntoVNode<'a> for LazyNodes<'a, '_> {
impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
self.call(cx)
}
+13 -3
View File
@@ -196,6 +196,7 @@ impl Scope {
let chan = self.sender.clone();
let id = self.scope_id();
Rc::new(move || {
log::debug!("set on channel an update for scope {:?}", id);
let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
})
}
@@ -303,10 +304,18 @@ impl Scope {
/// Pushes the future onto the poll queue to be polled
/// The future is forcibly dropped if the component is not ready by the next render
pub fn push_task<'src, F: Future<Output = ()> + 'src>(
pub fn push_task<'src, F: Future<Output = ()>>(
&'src self,
mut fut: impl FnOnce() -> F + 'src,
) -> usize {
fut: impl FnOnce() -> F + 'src,
) -> usize
where
F::Output: 'src,
F: 'src,
{
self.sender
.unbounded_send(SchedulerMsg::TaskPushed(self.our_arena_idx))
.unwrap();
// allocate the future
let fut = fut();
let fut: &mut dyn Future<Output = ()> = self.bump().alloc(fut);
@@ -318,6 +327,7 @@ impl Scope {
let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
let mut items = self.items.borrow_mut();
items.tasks.push(self_ref_fut);
items.tasks.len() - 1
}
+2 -4
View File
@@ -133,8 +133,6 @@ impl ScopeArena {
let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
debug_assert!(any_item.is_none());
new_scope_id
} else {
let (node_capacity, hook_capacity) = {
let heuristics = self.heuristics.borrow();
@@ -196,9 +194,9 @@ impl ScopeArena {
let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
debug_assert!(any_item.is_none());
new_scope_id
}
new_scope_id
}
pub fn try_remove(&self, id: &ScopeId) -> Option<()> {
+89 -104
View File
@@ -260,87 +260,45 @@ impl VirtualDom {
/// Waits for the scheduler to have work
/// This lets us poll async tasks during idle periods without blocking the main thread.
pub async fn wait_for_work(&mut self) {
// todo: poll the events once even if there is work to do to prevent starvation
// if there's no futures in the virtualdom, just wait for a scheduler message and put it into the queue to be processed
if self.scopes.pending_futures.borrow().is_empty() {
self.pending_messages
.push_front(self.receiver.next().await.unwrap());
} else {
struct PollTasks<'a> {
scopes: &'a mut ScopeArena,
loop {
if !self.dirty_scopes.is_empty() && self.pending_messages.is_empty() {
break;
}
impl<'a> Future for PollTasks<'a> {
type Output = ();
if self.pending_messages.is_empty() {
if self.scopes.pending_futures.borrow().is_empty() {
self.pending_messages
.push_front(self.receiver.next().await.unwrap());
} else {
use futures_util::future::{select, Either};
fn poll(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Self::Output> {
let mut all_pending = true;
let mut unfinished_tasks: SmallVec<[_; 10]> = smallvec::smallvec![];
let mut scopes_to_clear: SmallVec<[_; 10]> = smallvec::smallvec![];
// Poll every scope manually
for fut in self.scopes.pending_futures.borrow().iter() {
let scope = self
.scopes
.get_scope(fut)
.expect("Scope should never be moved");
let mut items = scope.items.borrow_mut();
// really this should just be retain_mut but that doesn't exist yet
while let Some(mut task) = items.tasks.pop() {
// todo: does this make sense?
// I don't usually write futures by hand
// I think the futures neeed to be pinned using bumpbox or something
// right now, they're bump allocated so this shouldn't matter anyway - they're not going to move
let task_mut = task.as_mut();
let unpinned = unsafe { Pin::new_unchecked(task_mut) };
if unpinned.poll(cx).is_ready() {
all_pending = false
} else {
unfinished_tasks.push(task);
}
}
if unfinished_tasks.is_empty() {
scopes_to_clear.push(*fut);
}
items.tasks.extend(unfinished_tasks.drain(..));
}
for scope in scopes_to_clear {
self.scopes.pending_futures.borrow_mut().remove(&scope);
}
// Resolve the future if any singular task is ready
match all_pending {
true => Poll::Pending,
false => Poll::Ready(()),
match select(PollTasks(&mut self.scopes), self.receiver.next()).await {
Either::Left((_, _)) => {}
Either::Right((msg, _)) => self.pending_messages.push_front(msg.unwrap()),
}
}
}
// Poll both the futures and the scheduler message queue simulataneously
use futures_util::future::{select, Either};
while let Ok(Some(msg)) = self.receiver.try_next() {
self.pending_messages.push_front(msg);
}
let scheduler_fut = self.receiver.next();
let tasks_fut = PollTasks {
scopes: &mut self.scopes,
};
match select(tasks_fut, scheduler_fut).await {
// Futures don't generate work
Either::Left((_, _)) => {}
// Save these messages in FIFO to be processed later
Either::Right((msg, _)) => self.pending_messages.push_front(msg.unwrap()),
if let Some(msg) = self.pending_messages.pop_back() {
match msg {
// just keep looping, the task is now saved but we should actually poll it
SchedulerMsg::TaskPushed(id) => {
self.scopes.pending_futures.borrow_mut().insert(id);
}
SchedulerMsg::UiEvent(event) => {
if let Some(element) = event.element {
self.scopes.call_listener_with_bubbling(event, element);
}
}
SchedulerMsg::Immediate(s) => {
self.dirty_scopes.insert(s);
}
SchedulerMsg::Suspended { scope } => todo!(),
}
}
}
}
@@ -393,36 +351,8 @@ impl VirtualDom {
pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec<Mutations> {
let mut committed_mutations = vec![];
while self.has_any_work() {
while let Ok(Some(msg)) = self.receiver.try_next() {
self.pending_messages.push_front(msg);
}
while let Some(msg) = self.pending_messages.pop_back() {
match msg {
// TODO: Suspsense
SchedulerMsg::Immediate(id) => {
self.dirty_scopes.insert(id);
}
SchedulerMsg::Suspended { node, scope } => {
todo!("should wire up suspense")
// self.suspense_scopes.insert(id);
}
SchedulerMsg::UiEvent(event) => {
if let Some(element) = event.element {
log::info!("Calling listener {:?}, {:?}", event.scope_id, element);
self.scopes.call_listener_with_bubbling(event, element);
while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
self.pending_messages.push_front(dirty_scope);
}
} else {
log::debug!("User event without a targetted ElementId. Not currently supported.\nUnsure how to proceed. {:?}", event);
}
}
}
}
while !self.dirty_scopes.is_empty() {
log::debug!("working with deadline");
let scopes = &self.scopes;
let mut diff_state = DiffState::new(scopes);
@@ -616,7 +546,10 @@ pub enum SchedulerMsg {
// setstate
Immediate(ScopeId),
Suspended { scope: ScopeId, node: NodeLink },
// an async task pushed from an event handler (or just spawned)
TaskPushed(ScopeId),
Suspended { scope: ScopeId },
}
/// User Events are events that are shuttled from the renderer into the VirtualDom trhough the scheduler channel.
@@ -720,3 +653,55 @@ pub enum EventPriority {
/// This is considered "idle" work or "background" work.
Low = 0,
}
struct PollTasks<'a>(&'a mut ScopeArena);
impl<'a> Future for PollTasks<'a> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
let mut all_pending = true;
let mut unfinished_tasks: SmallVec<[_; 10]> = smallvec::smallvec![];
let mut scopes_to_clear: SmallVec<[_; 10]> = smallvec::smallvec![];
// Poll every scope manually
for fut in self.0.pending_futures.borrow().iter() {
let scope = self.0.get_scope(fut).expect("Scope should never be moved");
let mut items = scope.items.borrow_mut();
// really this should just be retain_mut but that doesn't exist yet
while let Some(mut task) = items.tasks.pop() {
// todo: does this make sense?
// I don't usually write futures by hand
// I think the futures neeed to be pinned using bumpbox or something
// right now, they're bump allocated so this shouldn't matter anyway - they're not going to move
let task_mut = task.as_mut();
let unpinned = unsafe { Pin::new_unchecked(task_mut) };
if unpinned.poll(cx).is_ready() {
all_pending = false
} else {
unfinished_tasks.push(task);
}
}
if unfinished_tasks.is_empty() {
scopes_to_clear.push(*fut);
}
items.tasks.extend(unfinished_tasks.drain(..));
}
for scope in scopes_to_clear {
self.0.pending_futures.borrow_mut().remove(&scope);
}
// Resolve the future if any singular task is ready
match all_pending {
true => Poll::Pending,
false => Poll::Ready(()),
}
}
}