From 4b6ca05f2c3ad647842c858967da9c87f1915825 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 22 Sep 2021 04:11:27 -0400 Subject: [PATCH] wip: performance looks good, needs more testing --- packages/core/src/diff.rs | 5 ++--- packages/core/src/nodes.rs | 5 +++++ packages/core/src/scheduler.rs | 20 +++++++++--------- packages/hooks/src/usestate.rs | 8 +++---- packages/web/Cargo.toml | 1 + packages/web/examples/basic.rs | 38 +++++++++++++++++++++------------- packages/web/src/dom.rs | 8 +++---- packages/web/src/lib.rs | 2 -- packages/web/src/ric_raf.rs | 11 ++++++---- 9 files changed, 56 insertions(+), 42 deletions(-) diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index b360cba35..3e4ecfcfc 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -185,7 +185,6 @@ impl<'bump> DiffMachine<'bump> { /// Returns a `bool` indicating that the work completed properly. pub fn work(&mut self, mut deadline_expired: impl FnMut() -> bool) -> bool { while let Some(instruction) = self.stack.pop() { - log::debug!("working {:?}", instruction); match instruction { DiffInstruction::Diff { old, new } => self.diff_node(old, new), DiffInstruction::Create { node } => self.create_node(node), @@ -351,6 +350,7 @@ impl<'bump> DiffMachine<'bump> { // Insert a new scope into our component list let parent_scope = self.vdom.get_scope(parent_idx).unwrap(); + let new_idx = self.vdom.insert_scope_with_key(|new_idx| { let height = parent_scope.height + 1; Scope::new( @@ -475,7 +475,7 @@ impl<'bump> DiffMachine<'bump> { // TODO: take a more efficient path than this if old.attributes.len() == new.attributes.len() { for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) { - if old_attr.value != new_attr.value { + if old_attr.value != new_attr.value || old_attr.is_volatile { please_commit(&mut self.mutations.edits); self.mutations.set_attribute(new_attr); } @@ -664,7 +664,6 @@ impl<'bump> DiffMachine<'bump> { // the change list stack is in the same state when this function returns. fn diff_non_keyed_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) { // Handled these cases in `diff_children` before calling this function. - log::debug!("diffing non-keyed case"); debug_assert!(!new.is_empty()); debug_assert!(!old.is_empty()); diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 926a36d46..200bfa861 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -429,6 +429,11 @@ impl<'a> NodeFactory<'a> { is_volatile: bool, ) -> Attribute<'a> { let (value, is_static) = self.raw_text(val); + let is_volatile = match name { + "value" | "checked" | "selected" => true, + _ => false, + }; + Attribute { name, value, diff --git a/packages/core/src/scheduler.rs b/packages/core/src/scheduler.rs index 588b496a4..d64b6c651 100644 --- a/packages/core/src/scheduler.rs +++ b/packages/core/src/scheduler.rs @@ -379,7 +379,7 @@ impl Scheduler { log::debug!("Working finished? {:?}", work_completed); - log::debug!("raw edits {:?}", machine.mutations.edits); + // log::debug!("raw edits {:?}", machine.mutations.edits); let mut machine: DiffMachine<'static> = unsafe { std::mem::transmute(machine) }; // let mut saved = machine.save(); @@ -406,16 +406,16 @@ impl Scheduler { mutations.push(new_mutations); - log::debug!("saved edits {:?}", mutations); + // log::debug!("saved edits {:?}", mutations); let mut saved = machine.save(); self.save_work(saved); - false + true // self.save_work(saved); // false } else { - true + false } } @@ -461,7 +461,7 @@ impl Scheduler { while self.has_any_work() { // switch our priority, pop off any work - for event in self.ui_events.drain(..) { + while let Some(event) = self.ui_events.pop_front() { if let Some(scope) = self.pool.get_scope_mut(event.scope) { if let Some(element) = event.mounted_dom_id { log::info!("Calling listener {:?}, {:?}", event.scope, element); @@ -482,18 +482,18 @@ impl Scheduler { } } } - _ => todo!(), + SchedulerMsg::UiEvent(e) => self.ui_events.push_back(e), + SchedulerMsg::Task(_) => todo!(), } } } } } - let deadline_expired = - self.work_on_current_lane(&mut deadline, &mut committed_mutations); + let work_complete = self.work_on_current_lane(&mut deadline, &mut committed_mutations); - if deadline_expired { - break; + if !work_complete { + return committed_mutations; } } diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index bb5ac85ca..bd67fd153 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -56,7 +56,7 @@ pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T>( cx.use_hook( move |_| UseStateInner { current_val: initial_state_fn(), - callback: cx.schedule_update(), + update_callback: cx.schedule_update(), wip: Rc::new(RefCell::new(None)), update_scheuled: Cell::new(false), }, @@ -75,7 +75,7 @@ pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T>( struct UseStateInner { current_val: T, update_scheuled: Cell, - callback: Rc, + update_callback: Rc, wip: Rc>>, } @@ -98,13 +98,13 @@ impl<'a, T: 'static> UseState<'a, T> { pub fn needs_update(&self) { if !self.inner.update_scheuled.get() { self.inner.update_scheuled.set(true); - (self.inner.callback)(); + (self.inner.update_callback)(); } } pub fn set(&self, new_val: T) { - self.needs_update(); *self.inner.wip.borrow_mut() = Some(new_val); + self.needs_update(); } pub fn get(&self) -> &'a T { diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 7dc49bec9..9aa45c894 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -62,6 +62,7 @@ features = [ "SvgElement", "SvgAnimatedString", "HtmlOptionElement", + "IdleDeadline", ] [lib] diff --git a/packages/web/examples/basic.rs b/packages/web/examples/basic.rs index c41b34d7d..9539a2507 100644 --- a/packages/web/examples/basic.rs +++ b/packages/web/examples/basic.rs @@ -15,7 +15,7 @@ use std::{pin::Pin, time::Duration}; fn main() { // Setup logging - wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); + // wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); console_error_panic_hook::set_once(); // Run the app @@ -25,26 +25,34 @@ fn main() { static APP: FC<()> = |cx, props| { let mut count = use_state(cx, || 3); + let mut content = use_state(cx, || String::new()); cx.render(rsx! { div { + input { + value: "{content}" + oninput: move |e| { + content.set(e.value()); + } + } button { onclick: move |_| count += 1, "Click to add." "Current count: {count}" } + select { - name:"cars" - id:"cars" + name: "cars" + id: "cars" + value: "h1" oninput: move |ev| { match ev.value().as_str() { "h1" => count.set(0), "h2" => count.set(5), "h3" => count.set(10), - s => { - log::debug!("real value is {}", s); - } + s => {} } }, + option { value: "h1", "h1" } option { value: "h2", "h2" } option { value: "h3", "h3" } @@ -56,18 +64,20 @@ static APP: FC<()> = |cx, props| { li { "b - {f}" } li { "c - {f}" } })} + } + + {render_bullets(cx)} + Child {} } }) }; -static Child: FC<()> = |cx, props| { - cx.render(rsx! { - div { - div { - "hello child" - } - } +fn render_bullets(cx: Context) -> DomTree { + rsx!(cx, div { + "bite me" }) -}; +} + +static Child: FC<()> = |cx, props| rsx!(cx, div {"hello child"}); diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 56326b58d..97fc17132 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -520,8 +520,6 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> SyntheticEvent { /// This function decodes a websys event and produces an EventTrigger /// With the websys implementation, we attach a unique key to the nodes fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { - log::debug!("Handling event!"); - let target = event .target() .expect("missing target") @@ -535,7 +533,7 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { let attrs = target.attributes(); for x in 0..attrs.length() { let attr: Attr = attrs.item(x).unwrap(); - log::debug!("attrs include: {:#?}, {:#?}", attr.name(), attr.value()); + // log::debug!("attrs include: {:#?}, {:#?}", attr.name(), attr.value()); } use anyhow::Context; @@ -558,11 +556,11 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { .context("failed to parse real id")?; // Call the trigger - log::debug!("decoded scope_id: {}, node_id: {:#?}", gi_id, real_id); + // log::debug!("decoded scope_id: {}, node_id: {:#?}", gi_id, real_id); let triggered_scope = gi_id; // let triggered_scope: ScopeId = KeyData::from_ffi(gi_id).into(); - log::debug!("Triggered scope is {:#?}", triggered_scope); + // log::debug!("Triggered scope is {:#?}", triggered_scope); Ok(UserEvent { name: event_name_from_typ(&typ), event: virtual_event_from_websys_event(event.clone()), diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index c4ae4e9bf..6c4054b77 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -152,8 +152,6 @@ pub async fn run_with_props(root: FC, root_props: T, // run the virtualdom work phase until the frame deadline is reached let mutations = dom.run_with_deadline(|| (&mut deadline).now_or_never().is_some()); - log::debug!("received mutations {:#?}", mutations); - // wait for the animation frame to fire so we can apply our changes work_loop.wait_for_raf().await; diff --git a/packages/web/src/ric_raf.rs b/packages/web/src/ric_raf.rs index 08a3b89f5..d472a9866 100644 --- a/packages/web/src/ric_raf.rs +++ b/packages/web/src/ric_raf.rs @@ -8,7 +8,7 @@ use web_sys::Window; pub struct RafLoop { window: Window, - ric_receiver: async_channel::Receiver<()>, + ric_receiver: async_channel::Receiver, raf_receiver: async_channel::Receiver<()>, ric_closure: Closure, raf_closure: Closure, @@ -25,7 +25,10 @@ impl RafLoop { let (ric_sender, ric_receiver) = async_channel::unbounded(); let ric_closure: Closure = Closure::wrap(Box::new(move |_v: JsValue| { - ric_sender.try_send(()).unwrap() + // + let deadline = _v.dyn_into::().unwrap(); + let time_remaining = deadline.time_remaining() as u32; + ric_sender.try_send(time_remaining).unwrap() })); // execute the polyfill for safari @@ -46,8 +49,8 @@ impl RafLoop { /// waits for some idle time and returns a timeout future that expires after the idle time has passed pub async fn wait_for_idle_time(&self) -> TimeoutFuture { let ric_fn = self.ric_closure.as_ref().dyn_ref::().unwrap(); - let deadline: u32 = self.window.request_idle_callback(ric_fn).unwrap(); - self.ric_receiver.recv().await.unwrap(); + let _cb_id: u32 = self.window.request_idle_callback(ric_fn).unwrap(); + let deadline = self.ric_receiver.recv().await.unwrap(); let deadline = TimeoutFuture::new(deadline); deadline }