wip: performance looks good, needs more testing
This commit is contained in:
1 parent
84b5ddded5
commit
4b6ca05f2c
9 files changed
+56
-42
No files matched your search
@@ -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());
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 =
|
let work_complete = self.work_on_current_lane(&mut deadline, &mut committed_mutations);
|
||||||
self.work_on_current_lane(&mut deadline, &mut committed_mutations);
|
|
||||||
|
|
||||||
if deadline_expired {
|
if !work_complete {
|
||||||
break;
|
return committed_mutations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ features = [
|
|||||||
"SvgElement",
|
"SvgElement",
|
||||||
"SvgAnimatedString",
|
"SvgAnimatedString",
|
||||||
"HtmlOptionElement",
|
"HtmlOptionElement",
|
||||||
|
"IdleDeadline",
|
||||||
]
|
]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
@@ -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"
|
name: "cars"
|
||||||
id:"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 => {
|
s => {}
|
||||||
log::debug!("real value is {}", 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| {
|
fn render_bullets(cx: Context) -> DomTree {
|
||||||
cx.render(rsx! {
|
rsx!(cx, div {
|
||||||
div {
|
"bite me"
|
||||||
div {
|
|
||||||
"hello child"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
|
static Child: FC<()> = |cx, props| rsx!(cx, div {"hello child"});
|
||||||
@@ -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()),
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
let _cb_id: u32 = self.window.request_idle_callback(ric_fn).unwrap();
|
||||||
self.ric_receiver.recv().await.unwrap();
|
let deadline = self.ric_receiver.recv().await.unwrap();
|
||||||
let deadline = TimeoutFuture::new(deadline);
|
let deadline = TimeoutFuture::new(deadline);
|
||||||
deadline
|
deadline
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user