Better cache, fixed order for macro retrieve_from_cache

Better cache by saving last output and recomputing only when there is
a need.
This commit is contained in:
elvis
2025-10-23 01:47:49 +02:00
parent b5a8c8e283
commit 55c7675915
3 changed files with 82 additions and 51 deletions

View File

@ -4,7 +4,7 @@ set -eux
cargo check --workspace --all-targets cargo check --workspace --all-targets
cargo check --workspace --all-features --lib --target wasm32-unknown-unknown cargo check --workspace --all-features --lib --target wasm32-unknown-unknown
cargo fmt --all -- --check # cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::all cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::all
cargo test --workspace --all-targets --all-features cargo test --workspace --all-targets --all-features
cargo test --workspace --doc cargo test --workspace --doc

View File

@ -538,6 +538,7 @@ pub enum CustomResponse {
SetActiveNode(NodeId), SetActiveNode(NodeId),
ClearActiveNode, ClearActiveNode,
SaveToFile(NodeId), SaveToFile(NodeId),
FieldModified,
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]
@ -549,6 +550,7 @@ struct CacheInternals {
values: HashMap<OutputId, BasicValue>, values: HashMap<OutputId, BasicValue>,
hash_values: HashMap<OutputId, u64>, hash_values: HashMap<OutputId, u64>,
hash_inputs: HashMap<OutputId, (u64, Vec<u64>)>, hash_inputs: HashMap<OutputId, (u64, Vec<u64>)>,
last_output: Option<LayoutJob>,
} }
/// Cache used to save intermediate values between executions. /// Cache used to save intermediate values between executions.
@ -651,6 +653,21 @@ impl OutputsCache {
let mut internals = self.internals.write().unwrap(); let mut internals = self.internals.write().unwrap();
*internals = CacheInternals::default(); *internals = CacheInternals::default();
} }
pub fn get_last_state(&self) -> Option<LayoutJob> {
let internals = self.internals.read().unwrap();
internals.last_output.clone()
}
pub fn invalidate_last_state(&mut self) {
let mut internals = self.internals.write().unwrap();
internals.last_output = None;
}
pub fn set_last_state(&mut self, val: LayoutJob) {
let mut internals = self.internals.write().unwrap();
internals.last_output = Some(val);
}
} }
/// The graph 'global' state. /// The graph 'global' state.
@ -930,6 +947,8 @@ impl WidgetValueTrait for BasicValue {
_user_state: &mut GlobalState, _user_state: &mut GlobalState,
_node_data: &NodeData, _node_data: &NodeData,
) -> Vec<CustomResponse> { ) -> Vec<CustomResponse> {
let mut responses = vec![];
match self { match self {
// Dummy values used to save files, no ui since not needed // Dummy values used to save files, no ui since not needed
| BasicValue::SaveString { path: _, value: _ } => {}, | BasicValue::SaveString { path: _, value: _ } => {},
@ -938,21 +957,27 @@ impl WidgetValueTrait for BasicValue {
| BasicValue::String { value } => { | BasicValue::String { value } => {
ui.label(param_name); ui.label(param_name);
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add( let field = ui.add(
egui::TextEdit::multiline(value) egui::TextEdit::multiline(value)
.hint_text("String here") .hint_text("String here")
.clip_text(false), .clip_text(false),
); );
if field.changed() {
responses.push(CustomResponse::FieldModified);
}
}); });
}, },
| BasicValue::Path { value } => { | BasicValue::Path { value } => {
ui.label(param_name); ui.label(param_name);
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add( let field = ui.add(
egui::TextEdit::multiline(value) egui::TextEdit::multiline(value)
.hint_text("Path here") .hint_text("Path here")
.clip_text(false), .clip_text(false),
); );
if field.changed() {
responses.push(CustomResponse::FieldModified);
}
}); });
}, },
| BasicValue::System { value: _ } => { | BasicValue::System { value: _ } => {
@ -968,11 +993,14 @@ impl WidgetValueTrait for BasicValue {
| BasicValue::Symbol { value } => { | BasicValue::Symbol { value } => {
ui.label(param_name); ui.label(param_name);
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add( let field = ui.add(
egui::TextEdit::singleline(value) egui::TextEdit::singleline(value)
.hint_text("Symbol here") .hint_text("Symbol here")
.clip_text(false), .clip_text(false),
); );
if field.changed() {
responses.push(CustomResponse::FieldModified);
}
}); });
}, },
| BasicValue::Experiment { value: _ } => { | BasicValue::Experiment { value: _ } => {
@ -1051,8 +1079,8 @@ impl WidgetValueTrait for BasicValue {
}); });
}, },
} }
// Custom response (not used currently).
Vec::new() responses
} }
} }
@ -1075,20 +1103,12 @@ impl NodeDataTrait for NodeData {
where where
CustomResponse: UserResponseTrait, CustomResponse: UserResponseTrait,
{ {
// This logic is entirely up to the user. In this case, we check if the
// current node we're drawing is the active one, by comparing against
// the value stored in the global user state, and draw different button
// UIs based on that.
let mut responses = vec![]; let mut responses = vec![];
let is_active = user_state let is_active = user_state
.active_node .active_node
.map(|id| id == node_id) .map(|id| id == node_id)
.unwrap_or(false); .unwrap_or(false);
// Pressing the button will emit a custom user response to either set,
// or clear the active node. These responses do nothing by themselves,
// the library only makes the responses available to you after the graph
// has been drawn. See below at the update method for an example.
match (is_active, graph[node_id].user_data.template) { match (is_active, graph[node_id].user_data.template) {
| (_, NodeInstruction::SaveString) => { | (_, NodeInstruction::SaveString) => {
if ui.button("Write").clicked() { if ui.button("Write").clicked() {
@ -1293,37 +1313,58 @@ impl eframe::App for AppHandle {
}) })
.inner; .inner;
for node_response in graph_response.node_responses { for node_response in graph_response.node_responses.iter() {
// graph events // graph events
match node_response { match node_response {
| NodeResponse::User(CustomResponse::SetActiveNode(node)) => { | NodeResponse::User(CustomResponse::SetActiveNode(node)) => {
self.user_state.active_node = Some(node); self.user_state.active_node = Some(*node);
self.user_state.display_result = true; self.user_state.display_result = true;
self.user_state.cache.invalidate_last_state();
}, },
| NodeResponse::User(CustomResponse::ClearActiveNode) => { | NodeResponse::User(CustomResponse::ClearActiveNode) => {
self.user_state.active_node = None; self.user_state.active_node = None;
self.user_state.display_result = false; self.user_state.display_result = false;
self.user_state.cache.invalidate_last_state();
}, },
| NodeResponse::User(CustomResponse::SaveToFile(node)) => { | NodeResponse::User(CustomResponse::SaveToFile(node)) => {
self.user_state.save_node = Some(node); self.user_state.save_node = Some(*node);
self.user_state.display_result = true; self.user_state.display_result = true;
self.user_state.cache.invalidate_last_state();
}, },
| NodeResponse::DisconnectEvent { output, input: _ } => { | NodeResponse::DisconnectEvent { output, input: _ } => {
self.user_state.cache.invalidate_cache(&output); self.user_state.cache.invalidate_cache(output);
}, },
| NodeResponse::ConnectEventEnded { | NodeResponse::ConnectEventEnded {
output, output,
input: _, input: _,
input_hook: _, input_hook: _,
} => { } => {
self.user_state.cache.invalidate_cache(&output); self.user_state.cache.invalidate_cache(output);
}, },
| _ => {}, | _ => {},
} }
} }
if self.user_state.display_result { if self.user_state.display_result {
let text = create_output(self, ctx); let text = {
if !graph_response.node_responses.is_empty() {
let computed_output = create_output(self, ctx);
self.user_state
.cache
.set_last_state(computed_output.clone());
computed_output
} else if let Some(pre_computed) =
self.user_state.cache.get_last_state()
{
pre_computed
} else {
let computed_output = create_output(self, ctx);
self.user_state
.cache
.set_last_state(computed_output.clone());
computed_output
}
};
let window = egui::SidePanel::right("Results").resizable(true); let window = egui::SidePanel::right("Results").resizable(true);

View File

@ -184,105 +184,94 @@ fn process_template(
=> {retrieve_from_cache!(@as_expr ($($body)*))}; => {retrieve_from_cache!(@as_expr ($($body)*))};
(@accum (1) -> ($($body:tt)*)) (@accum (1) -> ($($body:tt)*))
=> {retrieve_from_cache!( => {retrieve_from_cache!(
@accum (0) -> ($($body)* @accum (0) -> (outputs_cache.retrieve_cache_output(
outputs_cache.retrieve_cache_output(
graph, graph,
node_id, node_id,
&graph[node_id] &graph[node_id]
.user_data .user_data
.template .template
.inputs()[0].0.clone())?,))}; .inputs()[0].0.clone())?, $($body)*))};
(@accum (2) -> ($($body:tt)*)) (@accum (2) -> ($($body:tt)*))
=> {retrieve_from_cache!( => {retrieve_from_cache!(
@accum (1) -> ($($body)* @accum (1) -> (outputs_cache.retrieve_cache_output(
outputs_cache.retrieve_cache_output(
graph, graph,
node_id, node_id,
&graph[node_id] &graph[node_id]
.user_data .user_data
.template .template
.inputs()[1].0.clone())?,))}; .inputs()[1].0.clone())?, $($body)*))};
(@accum (3) -> ($($body:tt)*)) (@accum (3) -> ($($body:tt)*))
=> {retrieve_from_cache!( => {retrieve_from_cache!(
@accum (2) -> ($($body)* @accum (2) -> (outputs_cache.retrieve_cache_output(
outputs_cache.retrieve_cache_output(
graph, graph,
node_id, node_id,
&graph[node_id] &graph[node_id]
.user_data .user_data
.template .template
.inputs()[2].0.clone())?,))}; .inputs()[2].0.clone())?, $($body)*))};
(@accum (4) -> ($($body:tt)*)) (@accum (4) -> ($($body:tt)*))
=> {retrieve_from_cache!( => {retrieve_from_cache!(
@accum (3) -> ($($body)* @accum (3) -> (outputs_cache.retrieve_cache_output(
outputs_cache.retrieve_cache_output(
graph, graph,
node_id, node_id,
&graph[node_id] &graph[node_id]
.user_data .user_data
.template .template
.inputs()[3].0.clone())?,))}; .inputs()[3].0.clone())?, $($body)*))};
(@accum (5) -> ($($body:tt)*)) (@accum (5) -> ($($body:tt)*))
=> {retrieve_from_cache!( => {retrieve_from_cache!(
@accum (4) -> ($($body)* @accum (4) -> (outputs_cache.retrieve_cache_output(
outputs_cache.retrieve_cache_output(
graph, graph,
node_id, node_id,
&graph[node_id] &graph[node_id]
.user_data .user_data
.template .template
.inputs()[4].0.clone())?,))}; .inputs()[4].0.clone())?, $($body)*))};
(@accum (6) -> ($($body:tt)*)) (@accum (6) -> ($($body:tt)*))
=> {retrieve_from_cache!( => {retrieve_from_cache!(
@accum (5) -> ($($body)* @accum (5) -> (outputs_cache.retrieve_cache_output(
outputs_cache.retrieve_cache_output(
graph, graph,
node_id, node_id,
&graph[node_id] &graph[node_id]
.user_data .user_data
.template .template
.inputs()[5].0.clone())?,))}; .inputs()[5].0.clone())?, $($body)*))};
(@accum (7) -> ($($body:tt)*)) (@accum (7) -> ($($body:tt)*))
=> {retrieve_from_cache!( => {retrieve_from_cache!(
@accum (6) -> ($($body)* @accum (6) -> (outputs_cache.retrieve_cache_output(
outputs_cache.retrieve_cache_output(
graph, graph,
node_id, node_id,
&graph[node_id] &graph[node_id]
.user_data .user_data
.template .template
.inputs()[6].0.clone())?,))}; .inputs()[6].0.clone())?, $($body)*))};
(@accum (8) -> ($($body:tt)*)) (@accum (8) -> ($($body:tt)*))
=> {retrieve_from_cache!( => {retrieve_from_cache!(
@accum (7) -> ($($body)* @accum (7) -> (outputs_cache.retrieve_cache_output(
outputs_cache.retrieve_cache_output(
graph, graph,
node_id, node_id,
&graph[node_id] &graph[node_id]
.user_data .user_data
.template .template
.inputs()[7].0.clone())?,))}; .inputs()[7].0.clone())?, $($body)*))};
(@accum (9) -> ($($body:tt)*)) (@accum (9) -> ($($body:tt)*))
=> {retrieve_from_cache!( => {retrieve_from_cache!(
@accum (8) -> ($($body)* @accum (8) -> (outputs_cache.retrieve_cache_output(
outputs_cache.retrieve_cache_output(
graph, graph,
node_id, node_id,
&graph[node_id] &graph[node_id]
.user_data .user_data
.template .template
.inputs()[8].0.clone())?,))}; .inputs()[8].0.clone())?, $($body)*))};
(@accum (10) -> ($($body:tt)*)) (@accum (10) -> ($($body:tt)*))
=> {retrieve_from_cache!( => {retrieve_from_cache!(
@accum (9) -> ($($body)* @accum (9) -> (outputs_cache.retrieve_cache_output(
outputs_cache.retrieve_cache_output(
graph, graph,
node_id, node_id,
&graph[node_id] &graph[node_id]
.user_data .user_data
.template .template
.inputs()[9].0.clone())?,))}; .inputs()[9].0.clone())?, $($body)*))};
(@as_expr $e:expr) => {$e}; (@as_expr $e:expr) => {$e};
[0] => { [0] => {
compile_error!("Macro returns a value or a tuple, supply an \ compile_error!("Macro returns a value or a tuple, supply an \
@ -895,6 +884,7 @@ fn process_template(
color_node, color_node,
color_edge, color_edge,
) = retrieve_from_cache![5]; ) = retrieve_from_cache![5];
let hash_inputs = hash_inputs!( let hash_inputs = hash_inputs!(
input_graph, input_graph,
display_node, display_node,