Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1fccb11
Add the core execution types, the borrowed context, and the seeded u6…
TrueDoctor Jul 31, 2026
5541769
Fix frame table key aliasing and arena handle offset truncation
TrueDoctor Jul 31, 2026
6feaf4e
Drop arena entries in reverse allocation order and require Send + Syn…
TrueDoctor Jul 31, 2026
0d0ec96
Draw arena generations from a global counter so foreign handles never…
TrueDoctor Jul 31, 2026
bdf5307
Forward serialize through node wrappers and free abandoned frame tabl…
TrueDoctor Jul 31, 2026
daf7b3e
Make arena generation exhaustion fallible with an unsafe counter rewind
TrueDoctor Jul 31, 2026
14c1dd0
Guard the shared generation counter in tests and reject oversized bat…
TrueDoctor Jul 31, 2026
0877f9d
Park the arena during drop glue, guard filled batches, and normalize …
TrueDoctor Jul 31, 2026
32d244e
Make the sources fields private and normalize them on every mutation
TrueDoctor Jul 31, 2026
1fb6fcb
Remove the unused CtxSnapshot scope reconstruction
TrueDoctor Jul 31, 2026
81a3194
Cut over to the graphene execution model
TrueDoctor Jul 31, 2026
c300545
Adapt the cutover to the reviewed core-types API
TrueDoctor Jul 31, 2026
77d439e
Silence the dead-code warnings for groundwork the host wiring will use
TrueDoctor Jul 31, 2026
e3aa32b
Fix wireing of async sources
TrueDoctor Aug 2, 2026
8686b2c
Export the async-source flag from the macro and dispatch superseded t…
TrueDoctor Aug 2, 2026
e372f78
Address pr review
TrueDoctor Aug 2, 2026
d385f3c
Fix warnings
TrueDoctor Aug 2, 2026
ee499be
Add test coverage for the repeat nodes
TrueDoctor Aug 2, 2026
167d733
Wire the async source runtimes into the hosts
TrueDoctor Jul 31, 2026
2b31617
Propagate tokio runtime creation failure in graphene-cli
TrueDoctor Aug 2, 2026
8fb1801
Remove unuse allow dead code
TrueDoctor Aug 2, 2026
562e660
Poll source tasks once at spawn and land inline completions in the fi…
TrueDoctor Aug 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions desktop/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ impl App {
});
let desktop_wrapper = DesktopWrapper::new(rand::rng().random(), Arc::new(resource_storage), dirs::app_autosave_documents_dir(), wgpu_context.clone(), wake);

let completion_render_sender = start_render_sender.clone();
DesktopWrapper::set_completion_notifier(move || {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
let _ = completion_render_sender.try_send(());
});

Self {
render_state: None,
wgpu_context,
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/render/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ impl RenderState {
return;
};
let size = glam::UVec2::new(viewport_texture.width(), viewport_texture.height());
let result = futures::executor::block_on(self.executor.render_vello_scene(&scene, size, &Default::default(), None));
let result = self.executor.render_vello_scene(&scene, size, &Default::default(), None);
match result {
Ok(texture) => {
self.overlays_texture = Some(texture.into());
Expand Down
4 changes: 4 additions & 0 deletions desktop/wrapper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ impl DesktopWrapper {
executor.execute()
}

pub fn set_completion_notifier(notifier: impl Fn() + Send + Sync + 'static) {
graphite_editor::node_graph_executor::set_completion_notifier(Arc::new(notifier));
}

pub async fn execute_node_graph() -> NodeGraphExecutionResult {
let result = graphite_editor::node_graph_executor::run_node_graph().await;
match result {
Expand Down
8 changes: 4 additions & 4 deletions document/graph-storage/src/tests/round_trip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,10 @@ fn test_nested_network_flattening() {
#[test]
fn test_metadata_preservation() {
// Create a network with nodes that have non-default metadata
let context_features = ContextDependencies {
extract: core_types::context::ContextFeatures::FOOTPRINT | core_types::context::ContextFeatures::REAL_TIME,
..Default::default()
};
let context_features = ContextDependencies::new(
core_types::context::ContextFeatures::FOOTPRINT | core_types::context::ContextFeatures::REAL_TIME,
core_types::context::ContextFeatures::empty(),
);

let network = NodeNetwork {
exports: vec![NodeInput::node(NodeId(1), 0)],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use graphene_std::memo::IORecord;
use graphene_std::raster_types::{CPU, GPU, Raster};
use graphene_std::vector::Vector;
use graphene_std::vector::style::{FillChoice, FillChoiceUI, GradientSpreadMethod, GradientType};
use graphene_std::{Artboard, Color, Context, Graphic};
use graphene_std::{Artboard, Color, CtxSnapshot, Graphic};
use std::any::Any;
use std::sync::Arc;

Expand Down Expand Up @@ -167,7 +167,7 @@ macro_rules! generate_layout_downcast {
($introspected_data:expr, $data:expr, [ $($ty:ty),* $(,)? ]) => {
if false { None }
$(
else if let Some(io) = $introspected_data.downcast_ref::<IORecord<Context, $ty>>() {
else if let Some(io) = $introspected_data.downcast_ref::<IORecord<CtxSnapshot, $ty>>() {
Some(io.output.layout_with_breadcrumb($data))
}
)*
Expand All @@ -178,7 +178,7 @@ macro_rules! generate_layout_downcast {
fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'static>, data: &mut LayoutData) -> Option<Vec<LayoutGroup>> {
// `List<NodeId>` is interpreted as a path (e.g. the value produced by `path_of_subgraph`), shown as a
// `List` where each item's NodeId resolves against the prefix made up of the items above it.
if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, List<NodeId>>>() {
if let Some(io) = introspected_data.downcast_ref::<IORecord<CtxSnapshot, List<NodeId>>>() {
return Some(table_node_id_path_layout_with_breadcrumb(&io.output, data));
}
generate_layout_downcast!(introspected_data, data, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,12 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
exports: vec![NodeInput::node(NodeId(1), 0)],
nodes: [
DocumentNode {
inputs: vec![NodeInput::value(TaggedValue::None, false), NodeInput::import(concrete!(String), 1)],
inputs: vec![
NodeInput::value(TaggedValue::None, false),
NodeInput::import(concrete!(String), 1),
NodeInput::scope("graphene_std::runtime::RuntimeNode"),
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::SourceId),
],
implementation: DocumentNodeImplementation::ProtoNode(platform_application_io::load_resource::IDENTIFIER),
..Default::default()
},
Expand Down Expand Up @@ -994,7 +999,13 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::import(generic!(T), 0), NodeInput::import(concrete!(Footprint), 1), NodeInput::node(NodeId(1), 0)],
inputs: vec![
NodeInput::import(generic!(T), 0),
NodeInput::import(concrete!(Footprint), 1),
NodeInput::node(NodeId(1), 0),
NodeInput::scope("graphene_std::runtime::RuntimeNode"),
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::SourceId),
],
implementation: DocumentNodeImplementation::ProtoNode(platform_application_io::rasterize::IDENTIFIER),
..Default::default()
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap
// fallback when deriving `call_argument` so it reflects the impls actually registered, which will usually be `Context`.
let extended_node_registry = &*interpreted_executor::node_registry::NODE_REGISTRY;
let node_registry = NODE_REGISTRY.lock().unwrap();
let empty_implementations: Vec<(NodeConstructor, NodeIOTypes)> = Vec::new();
let empty_implementations: Vec<RegistryEntry> = Vec::new();
let context_type = concrete!(Context);
for (id, metadata) in NODE_METADATA.lock().unwrap().iter() {
let identifier = DefinitionIdentifier::ProtoNode(id.clone());
Expand All @@ -48,12 +48,12 @@ pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap

let implementations = node_registry.get(id).unwrap_or(&empty_implementations);

let first_node_io = implementations.first().map(|(_, node_io)| node_io).unwrap_or(const { &NodeIOTypes::empty() });
let first_node_io = implementations.first().map(|entry| &entry.io).unwrap_or(const { &NodeIOTypes::empty() });

let call_arguments: Vec<&Type> = if !implementations.is_empty() {
implementations.iter().map(|(_, io)| &io.call_argument).collect()
implementations.iter().map(|entry| &entry.io.call_argument).collect()
} else if let Some(impls) = extended_node_registry.get(id) {
impls.keys().map(|io| &io.call_argument).collect()
impls.iter().map(|entry| &entry.io.call_argument).collect()
} else {
Vec::new()
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ pub(crate) fn property_from_type(
Type::Generic(_) => vec![TextLabel::new("Generic Type (Not Supported)").widget_instance()].into(),
Type::Fn(_, out) => return property_from_type(node_id, index, out, number_options, unit, display_decimal_places, step, context),
Type::Future(out) => return property_from_type(node_id, index, out, number_options, unit, display_decimal_places, step, context),
Type::Ref(inner) => return property_from_type(node_id, index, inner, number_options, unit, display_decimal_places, step, context),
};

extra_widgets.push(widgets);
Expand Down Expand Up @@ -2359,7 +2360,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
return Vec::new();
};

let mut input_types = implementations.keys().filter_map(|item| item.inputs.get(input_index)).collect::<Vec<_>>();
let mut input_types = implementations.iter().filter_map(|entry| entry.io.inputs.get(input_index)).collect::<Vec<_>>();
input_types.sort_by_key(|ty| ty.type_name());
let input_type = input_types.first().cloned();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,9 @@ impl NodeNetworkInterface {
};
let number_of_inputs = self.number_of_inputs(node_id, network_path);
implementations
.keys()
.filter_map(|node_io| {
.iter()
.filter_map(|entry| {
let node_io = &entry.io;
// Check if this NodeIOTypes implementation is valid for the other inputs
let valid_implementation = (0..number_of_inputs).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| {
let input_type = self.input_type_not_invalid(&InputConnector::node(*node_id, iterator_index), network_path);
Expand Down Expand Up @@ -293,8 +294,9 @@ impl NodeNetworkInterface {
let valid_output_types = self.valid_output_types(&OutputConnector::node(*node_id, 0), network_path);

implementations
.keys()
.filter_map(|node_io| {
.iter()
.filter_map(|entry| {
let node_io = &entry.io;
if !valid_output_types.iter().any(|output_type| output_type.nested_type() == node_io.return_value.nested_type()) {
return None;
}
Expand Down Expand Up @@ -323,7 +325,7 @@ impl NodeNetworkInterface {
log::error!("Protonode {render_node:?} not found in registry");
return Vec::new();
};
implementations.keys().map(|types| types.inputs[1].clone()).collect()
implementations.iter().map(|entry| entry.io.inputs[1].clone()).collect()
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion editor/src/messages/portfolio/document_migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2406,7 +2406,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
&& let Some(reference) = document.network_interface.reference(node_id, network_path).clone()
&& let Some(node_definition) = resolve_document_node_type(&reference)
{
let context_features = node_definition.node_template.document_node.context_features;
let context_features = node_definition.node_template.document_node.context_features.clone();
document.network_interface.set_context_features(node_id, network_path, context_features);
}

Expand Down
32 changes: 24 additions & 8 deletions editor/src/node_graph_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use graphene_std::raster::{CPU, Raster};
use graphene_std::renderer::{RenderMetadata, graphic_list_bounding_box};
use graphene_std::transform::Footprint;
use graphene_std::vector::{Vector, graphic_types};
use graphene_std::{ATTR_TRANSFORM, Context, Graphic, NodeInputDecleration};
use graphene_std::{ATTR_TRANSFORM, CtxSnapshot, Graphic, NodeInputDecleration};
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
use std::any::Any;
use std::sync::Arc;
Expand All @@ -26,7 +26,7 @@ pub use runtime_io::NodeRuntimeIO;
mod runtime;
pub use runtime::*;

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ExecutionRequest {
execution_id: u64,
render_config: RenderConfig,
Expand Down Expand Up @@ -59,6 +59,9 @@ pub struct NodeGraphExecutor {
runtime_io: NodeRuntimeIO,
current_execution_id: u64,
futures: VecDeque<(u64, ExecutionContext)>,
/// The most recently consumed plain render execution, kept so a runtime-replayed response with the same id
/// (sent after an async source completion) finds its context again.
last_execution_context: Option<(u64, ExecutionContext)>,
node_graph_hash: u64,
/// Full path from the root document network to the node currently being inspected by the Data panel, or empty if nothing is selected.
/// The last element is the inspect target itself; preceding elements identify the nested subnetwork the node lives in,
Expand Down Expand Up @@ -108,6 +111,7 @@ impl NodeGraphExecutor {
let node_executor = Self {
futures: Default::default(),
runtime_io: NodeRuntimeIO::with_channels(request_sender, response_receiver),
last_execution_context: None,
node_graph_hash: 0,
current_execution_id: 0,
previous_node_to_inspect: Vec::new(),
Expand Down Expand Up @@ -375,10 +379,22 @@ impl NodeGraphExecutor {
}
}

let Some((queued_execution_id, execution_context)) = self.futures.pop_front() else {
panic!("InvalidGenerationId")
let execution_context = if self.futures.front().is_some_and(|&(queued_execution_id, _)| queued_execution_id == execution_id) {
let (_, execution_context) = self.futures.pop_front().expect("front was just matched");
self.last_execution_context = Some((execution_id, execution_context.clone()));
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
execution_context
} else {
// A runtime-replayed response re-uses an already consumed id; only plain renders may re-apply.
match &self.last_execution_context {
Some((last_execution_id, execution_context)) if *last_execution_id == execution_id => {
if execution_context.export_config.is_some() || execution_context.measure_fill.is_some() {
continue;
}
execution_context.clone()
}
_ => panic!("InvalidGenerationId"),
}
};
assert_eq!(queued_execution_id, execution_id, "Missmatch in execution id");

// TODO: Eventually remove this document upgrade code
// Gradient-migration measurement runs only read back the fill's evaluated geometry; they never render to the artwork.
Expand Down Expand Up @@ -892,7 +908,7 @@ fn introspected_output<T: Clone + Send + Sync + 'static>(data: &Arc<dyn Any + Se
if let Some(io) = data.downcast_ref::<IORecord<Footprint, T>>() {
return Some(io.output.clone());
}
if let Some(io) = data.downcast_ref::<IORecord<Context, T>>() {
if let Some(io) = data.downcast_ref::<IORecord<CtxSnapshot, T>>() {
return Some(io.output.clone());
}
None
Expand All @@ -911,7 +927,7 @@ mod test {
use crate::test_utils::test_prelude::{self, NodeGraphLayer};
use graph_craft::ProtoNodeIdentifier;
use graph_craft::document::NodeNetwork;
use graphene_std::Context;
use graphene_std::CtxSnapshot;
use graphene_std::NodeInputDecleration;
use graphene_std::memo::IORecord;
use test_prelude::LayerNodeIdentifier;
Expand Down Expand Up @@ -979,7 +995,7 @@ mod test {
Some(x.output.clone())
} else if let Some(x) = dynamic.downcast_ref::<IORecord<Footprint, Input::Result>>() {
Some(x.output.clone())
} else if let Some(x) = dynamic.downcast_ref::<IORecord<Context, Input::Result>>() {
} else if let Some(x) = dynamic.downcast_ref::<IORecord<CtxSnapshot, Input::Result>>() {
Some(x.output.clone())
} else {
warn!("cannot downcast type for introspection");
Expand Down
Loading
Loading