Browse Source

wip: performance looks good, needs more testing

Jonathan Kelley 3 years ago
parent
commit
4b6ca05

+ 2 - 3
packages/core/src/diff.rs

@@ -185,7 +185,6 @@ impl<'bump> DiffMachine<'bump> {
     /// Returns a `bool` indicating that the work completed properly.
     /// Returns a `bool` indicating that the work completed properly.
     pub fn work(&mut self, mut deadline_expired: impl FnMut() -> bool) -> bool {
     pub fn work(&mut self, mut deadline_expired: impl FnMut() -> bool) -> bool {
         while let Some(instruction) = self.stack.pop() {
         while let Some(instruction) = self.stack.pop() {
-            log::debug!("working {:?}", instruction);
             match instruction {
             match instruction {
                 DiffInstruction::Diff { old, new } => self.diff_node(old, new),
                 DiffInstruction::Diff { old, new } => self.diff_node(old, new),
                 DiffInstruction::Create { node } => self.create_node(node),
                 DiffInstruction::Create { node } => self.create_node(node),
@@ -351,6 +350,7 @@ impl<'bump> DiffMachine<'bump> {
 
 
         // Insert a new scope into our component list
         // Insert a new scope into our component list
         let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
         let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
+
         let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
         let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
             let height = parent_scope.height + 1;
             let height = parent_scope.height + 1;
             Scope::new(
             Scope::new(
@@ -475,7 +475,7 @@ impl<'bump> DiffMachine<'bump> {
         // TODO: take a more efficient path than this
         // TODO: take a more efficient path than this
         if old.attributes.len() == new.attributes.len() {
         if old.attributes.len() == new.attributes.len() {
             for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
             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);
                     please_commit(&mut self.mutations.edits);
                     self.mutations.set_attribute(new_attr);
                     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.
     // 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>]) {
     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.
         // Handled these cases in `diff_children` before calling this function.
-        log::debug!("diffing non-keyed case");
         debug_assert!(!new.is_empty());
         debug_assert!(!new.is_empty());
         debug_assert!(!old.is_empty());
         debug_assert!(!old.is_empty());
 
 

+ 5 - 0
packages/core/src/nodes.rs

@@ -429,6 +429,11 @@ impl<'a> NodeFactory<'a> {
         is_volatile: bool,
         is_volatile: bool,
     ) -> Attribute<'a> {
     ) -> Attribute<'a> {
         let (value, is_static) = self.raw_text(val);
         let (value, is_static) = self.raw_text(val);
+        let is_volatile = match name {
+            "value" | "checked" | "selected" => true,
+            _ => false,
+        };
+
         Attribute {
         Attribute {
             name,
             name,
             value,
             value,

+ 10 - 10
packages/core/src/scheduler.rs

@@ -379,7 +379,7 @@ impl Scheduler {
 
 
         log::debug!("Working finished? {:?}", work_completed);
         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 machine: DiffMachine<'static> = unsafe { std::mem::transmute(machine) };
         // let mut saved = machine.save();
         // let mut saved = machine.save();
@@ -406,16 +406,16 @@ impl Scheduler {
 
 
             mutations.push(new_mutations);
             mutations.push(new_mutations);
 
 
-            log::debug!("saved edits {:?}", mutations);
+            // log::debug!("saved edits {:?}", mutations);
 
 
             let mut saved = machine.save();
             let mut saved = machine.save();
             self.save_work(saved);
             self.save_work(saved);
-            false
+            true
 
 
             // self.save_work(saved);
             // self.save_work(saved);
             // false
             // false
         } else {
         } else {
-            true
+            false
         }
         }
     }
     }
 
 
@@ -461,7 +461,7 @@ impl Scheduler {
 
 
         while self.has_any_work() {
         while self.has_any_work() {
             // switch our priority, pop off 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(scope) = self.pool.get_scope_mut(event.scope) {
                     if let Some(element) = event.mounted_dom_id {
                     if let Some(element) = event.mounted_dom_id {
                         log::info!("Calling listener {:?}, {:?}", event.scope, element);
                         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;
             }
             }
         }
         }
 
 

+ 4 - 4
packages/hooks/src/usestate.rs

@@ -56,7 +56,7 @@ pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T>(
     cx.use_hook(
     cx.use_hook(
         move |_| UseStateInner {
         move |_| UseStateInner {
             current_val: initial_state_fn(),
             current_val: initial_state_fn(),
-            callback: cx.schedule_update(),
+            update_callback: cx.schedule_update(),
             wip: Rc::new(RefCell::new(None)),
             wip: Rc::new(RefCell::new(None)),
             update_scheuled: Cell::new(false),
             update_scheuled: Cell::new(false),
         },
         },
@@ -75,7 +75,7 @@ pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T>(
 struct UseStateInner<T: 'static> {
 struct UseStateInner<T: 'static> {
     current_val: T,
     current_val: T,
     update_scheuled: Cell<bool>,
     update_scheuled: Cell<bool>,
-    callback: Rc<dyn Fn()>,
+    update_callback: Rc<dyn Fn()>,
     wip: Rc<RefCell<Option<T>>>,
     wip: Rc<RefCell<Option<T>>>,
 }
 }
 
 
@@ -98,13 +98,13 @@ impl<'a, T: 'static> UseState<'a, T> {
     pub fn needs_update(&self) {
     pub fn needs_update(&self) {
         if !self.inner.update_scheuled.get() {
         if !self.inner.update_scheuled.get() {
             self.inner.update_scheuled.set(true);
             self.inner.update_scheuled.set(true);
-            (self.inner.callback)();
+            (self.inner.update_callback)();
         }
         }
     }
     }
 
 
     pub fn set(&self, new_val: T) {
     pub fn set(&self, new_val: T) {
-        self.needs_update();
         *self.inner.wip.borrow_mut() = Some(new_val);
         *self.inner.wip.borrow_mut() = Some(new_val);
+        self.needs_update();
     }
     }
 
 
     pub fn get(&self) -> &'a T {
     pub fn get(&self) -> &'a T {

+ 1 - 0
packages/web/Cargo.toml

@@ -62,6 +62,7 @@ features = [
     "SvgElement",
     "SvgElement",
     "SvgAnimatedString",
     "SvgAnimatedString",
     "HtmlOptionElement",
     "HtmlOptionElement",
+    "IdleDeadline",
 ]
 ]
 
 
 [lib]
 [lib]

+ 24 - 14
packages/web/examples/basic.rs

@@ -15,7 +15,7 @@ use std::{pin::Pin, time::Duration};
 
 
 fn main() {
 fn main() {
     // Setup logging
     // 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();
     console_error_panic_hook::set_once();
 
 
     // Run the app
     // Run the app
@@ -25,26 +25,34 @@ fn main() {
 static APP: FC<()> = |cx, props| {
 static APP: FC<()> = |cx, props| {
     let mut count = use_state(cx, || 3);
     let mut count = use_state(cx, || 3);
 
 
+    let mut content = use_state(cx, || String::new());
     cx.render(rsx! {
     cx.render(rsx! {
         div {
         div {
+            input {
+                value: "{content}"
+                oninput: move |e| {
+                    content.set(e.value());
+                }
+            }
             button {
             button {
                 onclick: move |_| count += 1,
                 onclick: move |_| count += 1,
                 "Click to add."
                 "Click to add."
                 "Current count: {count}"
                 "Current count: {count}"
             }
             }
+
             select {
             select {
-                name:"cars"
-                id:"cars"
+                name: "cars"
+                id: "cars"
+                value: "h1"
                 oninput: move |ev| {
                 oninput: move |ev| {
                     match ev.value().as_str() {
                     match ev.value().as_str() {
                         "h1" => count.set(0),
                         "h1" => count.set(0),
                         "h2" => count.set(5),
                         "h2" => count.set(5),
                         "h3" => count.set(10),
                         "h3" => count.set(10),
-                        s => {
-                            log::debug!("real value is {}", s);
-                        }
+                        s => {}
                     }
                     }
                 },
                 },
+
                 option { value: "h1", "h1" }
                 option { value: "h1", "h1" }
                 option { value: "h2", "h2" }
                 option { value: "h2", "h2" }
                 option { value: "h3", "h3" }
                 option { value: "h3", "h3" }
@@ -56,18 +64,20 @@ static APP: FC<()> = |cx, props| {
                     li { "b - {f}" }
                     li { "b - {f}" }
                     li { "c - {f}" }
                     li { "c - {f}" }
                 })}
                 })}
+
             }
             }
+
+            {render_bullets(cx)}
+
             Child {}
             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"});

+ 3 - 5
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
 /// This function decodes a websys event and produces an EventTrigger
 /// With the websys implementation, we attach a unique key to the nodes
 /// With the websys implementation, we attach a unique key to the nodes
 fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
 fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
-    log::debug!("Handling event!");
-
     let target = event
     let target = event
         .target()
         .target()
         .expect("missing target")
         .expect("missing target")
@@ -535,7 +533,7 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
     let attrs = target.attributes();
     let attrs = target.attributes();
     for x in 0..attrs.length() {
     for x in 0..attrs.length() {
         let attr: Attr = attrs.item(x).unwrap();
         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;
     use anyhow::Context;
@@ -558,11 +556,11 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
         .context("failed to parse real id")?;
         .context("failed to parse real id")?;
 
 
     // Call the trigger
     // 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 = gi_id;
     // let triggered_scope: ScopeId = KeyData::from_ffi(gi_id).into();
     // 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 {
     Ok(UserEvent {
         name: event_name_from_typ(&typ),
         name: event_name_from_typ(&typ),
         event: virtual_event_from_websys_event(event.clone()),
         event: virtual_event_from_websys_event(event.clone()),

+ 0 - 2
packages/web/src/lib.rs

@@ -152,8 +152,6 @@ pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T,
         // run the virtualdom work phase until the frame deadline is reached
         // run the virtualdom work phase until the frame deadline is reached
         let mutations = dom.run_with_deadline(|| (&mut deadline).now_or_never().is_some());
         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
         // wait for the animation frame to fire so we can apply our changes
         work_loop.wait_for_raf().await;
         work_loop.wait_for_raf().await;
 
 

+ 7 - 4
packages/web/src/ric_raf.rs

@@ -8,7 +8,7 @@ use web_sys::Window;
 
 
 pub struct RafLoop {
 pub struct RafLoop {
     window: Window,
     window: Window,
-    ric_receiver: async_channel::Receiver<()>,
+    ric_receiver: async_channel::Receiver<u32>,
     raf_receiver: async_channel::Receiver<()>,
     raf_receiver: async_channel::Receiver<()>,
     ric_closure: Closure<dyn Fn(JsValue)>,
     ric_closure: Closure<dyn Fn(JsValue)>,
     raf_closure: Closure<dyn Fn(JsValue)>,
     raf_closure: Closure<dyn Fn(JsValue)>,
@@ -25,7 +25,10 @@ impl RafLoop {
         let (ric_sender, ric_receiver) = async_channel::unbounded();
         let (ric_sender, ric_receiver) = async_channel::unbounded();
 
 
         let ric_closure: Closure<dyn Fn(JsValue)> = Closure::wrap(Box::new(move |_v: JsValue| {
         let ric_closure: Closure<dyn Fn(JsValue)> = Closure::wrap(Box::new(move |_v: JsValue| {
-            ric_sender.try_send(()).unwrap()
+            //
+            let deadline = _v.dyn_into::<web_sys::IdleDeadline>().unwrap();
+            let time_remaining = deadline.time_remaining() as u32;
+            ric_sender.try_send(time_remaining).unwrap()
         }));
         }));
 
 
         // execute the polyfill for safari
         // 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
     /// 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 {
     pub async fn wait_for_idle_time(&self) -> TimeoutFuture {
         let ric_fn = self.ric_closure.as_ref().dyn_ref::<Function>().unwrap();
         let ric_fn = self.ric_closure.as_ref().dyn_ref::<Function>().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);
         let deadline = TimeoutFuture::new(deadline);
         deadline
         deadline
     }
     }