mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 23:38:06 +08:00
Restore functionality of GPU infrastructure (#1797)
* Update gpu nodes to compile again Restructure `gpu-executor` and `wgpu-executor` And libssl to nix shell Fix graphene-cli and add half percision color format Fix texture scaling Remove vulkan executor Fix compile errors Improve execution request deduplication * Fix warnings * Fix graph compile issues * Code review * Remove test file * Fix lint * Wip make node futures send * Make futures Send on non wasm targets * Fix warnings * Fix nested use of block_on --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -4,6 +4,7 @@ use crate::messages::portfolio::document::node_graph::document_node_types::wrap_
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use futures::lock::Mutex;
|
||||
use graph_craft::concrete;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeNetwork};
|
||||
@@ -24,11 +25,7 @@ use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
|
||||
use interpreted_executor::dynamic_executor::{DynamicExecutor, ResolvedDocumentNodeTypes};
|
||||
|
||||
use glam::{DAffine2, DVec2, UVec2};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hash;
|
||||
use std::hash::Hasher;
|
||||
use std::rc::Rc;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -43,8 +40,6 @@ pub struct NodeRuntime {
|
||||
recompile_graph: bool,
|
||||
|
||||
editor_api: Arc<WasmEditorApi>,
|
||||
|
||||
graph_hash: Option<u64>,
|
||||
node_graph_errors: GraphErrors,
|
||||
resolved_types: ResolvedDocumentNodeTypes,
|
||||
monitor_nodes: Vec<Vec<NodeId>>,
|
||||
@@ -62,6 +57,7 @@ pub struct NodeRuntime {
|
||||
|
||||
/// Messages passed from the editor thread to the node runtime thread.
|
||||
pub enum NodeRuntimeMessage {
|
||||
GraphUpdate(NodeNetwork),
|
||||
ExecutionRequest(ExecutionRequest),
|
||||
FontCacheUpdate(FontCache),
|
||||
ImaginatePreferencesUpdate(ImaginatePreferences),
|
||||
@@ -79,7 +75,6 @@ pub struct ExportConfig {
|
||||
|
||||
pub struct ExecutionRequest {
|
||||
execution_id: u64,
|
||||
graph: NodeNetwork,
|
||||
render_config: RenderConfig,
|
||||
}
|
||||
|
||||
@@ -90,13 +85,18 @@ pub struct ExecutionResponse {
|
||||
new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
|
||||
new_vector_modify: HashMap<NodeId, VectorData>,
|
||||
new_upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
|
||||
transform: DAffine2,
|
||||
}
|
||||
|
||||
pub struct CompilationResponse {
|
||||
result: Result<(), String>,
|
||||
resolved_types: ResolvedDocumentNodeTypes,
|
||||
node_graph_errors: GraphErrors,
|
||||
transform: DAffine2,
|
||||
}
|
||||
|
||||
pub enum NodeGraphUpdate {
|
||||
ExecutionResponse(ExecutionResponse),
|
||||
CompilationResponse(CompilationResponse),
|
||||
NodeGraphUpdateMessage(NodeGraphUpdateMessage),
|
||||
}
|
||||
|
||||
@@ -104,7 +104,11 @@ pub enum NodeGraphUpdate {
|
||||
struct InternalNodeGraphUpdateSender(Sender<NodeGraphUpdate>);
|
||||
|
||||
impl InternalNodeGraphUpdateSender {
|
||||
fn send_generation_response(&self, response: ExecutionResponse) {
|
||||
fn send_generation_response(&self, response: CompilationResponse) {
|
||||
self.0.send(NodeGraphUpdate::CompilationResponse(response)).expect("Failed to send response")
|
||||
}
|
||||
|
||||
fn send_execution_response(&self, response: ExecutionResponse) {
|
||||
self.0.send(NodeGraphUpdate::ExecutionResponse(response)).expect("Failed to send response")
|
||||
}
|
||||
}
|
||||
@@ -115,9 +119,7 @@ impl NodeGraphUpdateSender for InternalNodeGraphUpdateSender {
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
pub(crate) static NODE_RUNTIME: Rc<RefCell<Option<NodeRuntime>>> = Rc::new(RefCell::new(None));
|
||||
}
|
||||
pub(crate) static NODE_RUNTIME: Lazy<Mutex<Option<NodeRuntime>>> = Lazy::new(|| Mutex::new(None));
|
||||
|
||||
impl NodeRuntime {
|
||||
pub fn new(receiver: Receiver<NodeRuntimeMessage>, sender: Sender<NodeGraphUpdate>) -> Self {
|
||||
@@ -137,7 +139,6 @@ impl NodeRuntime {
|
||||
}
|
||||
.into(),
|
||||
|
||||
graph_hash: None,
|
||||
node_graph_errors: Vec::new(),
|
||||
resolved_types: ResolvedDocumentNodeTypes::default(),
|
||||
monitor_nodes: Vec::new(),
|
||||
@@ -150,12 +151,22 @@ impl NodeRuntime {
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
let mut requests = self.receiver.try_iter().collect::<Vec<_>>();
|
||||
// TODO: Currently we still render the document after we submit the node graph execution request.
|
||||
// This should be avoided in the future.
|
||||
requests.reverse();
|
||||
requests.dedup_by(|a, b| matches!(a, NodeRuntimeMessage::ExecutionRequest(_)) && matches!(b, NodeRuntimeMessage::ExecutionRequest(_)));
|
||||
requests.reverse();
|
||||
// TODO: Currently we still render the document after we submit the node graph execution request. This should be avoided in the future.
|
||||
|
||||
let mut font = None;
|
||||
let mut imaginate = None;
|
||||
let mut graph = None;
|
||||
let mut execution = None;
|
||||
for request in self.receiver.try_iter() {
|
||||
match request {
|
||||
NodeRuntimeMessage::GraphUpdate(_) => graph = Some(request),
|
||||
NodeRuntimeMessage::ExecutionRequest(_) => execution = Some(request),
|
||||
NodeRuntimeMessage::FontCacheUpdate(_) => font = Some(request),
|
||||
NodeRuntimeMessage::ImaginatePreferencesUpdate(_) => imaginate = Some(request),
|
||||
}
|
||||
}
|
||||
let requests = [font, imaginate, graph, execution].into_iter().flatten();
|
||||
|
||||
for request in requests {
|
||||
match request {
|
||||
NodeRuntimeMessage::FontCacheUpdate(font_cache) => {
|
||||
@@ -178,25 +189,31 @@ impl NodeRuntime {
|
||||
.into();
|
||||
self.recompile_graph = true;
|
||||
}
|
||||
NodeRuntimeMessage::ExecutionRequest(ExecutionRequest {
|
||||
execution_id, graph, render_config, ..
|
||||
}) => {
|
||||
NodeRuntimeMessage::GraphUpdate(graph) => {
|
||||
self.node_graph_errors.clear();
|
||||
let result = self.update_network(graph).await;
|
||||
self.sender.send_generation_response(CompilationResponse {
|
||||
result,
|
||||
resolved_types: self.resolved_types.clone(),
|
||||
node_graph_errors: self.node_graph_errors.clone(),
|
||||
});
|
||||
self.recompile_graph = true;
|
||||
}
|
||||
NodeRuntimeMessage::ExecutionRequest(ExecutionRequest { execution_id, render_config, .. }) => {
|
||||
let transform = render_config.viewport.transform;
|
||||
|
||||
let result = self.execute_network(graph, render_config).await;
|
||||
let result = self.execute_network(render_config).await;
|
||||
|
||||
let mut responses = VecDeque::new();
|
||||
self.process_monitor_nodes(&mut responses);
|
||||
|
||||
self.sender.send_generation_response(ExecutionResponse {
|
||||
self.sender.send_execution_response(ExecutionResponse {
|
||||
execution_id,
|
||||
result,
|
||||
responses,
|
||||
new_click_targets: self.click_targets.clone().into_iter().map(|(id, targets)| (LayerNodeIdentifier::new_unchecked(id), targets)).collect(),
|
||||
new_vector_modify: self.vector_modify.clone(),
|
||||
new_upstream_transforms: self.upstream_transforms.clone(),
|
||||
resolved_types: self.resolved_types.clone(),
|
||||
node_graph_errors: core::mem::take(&mut self.node_graph_errors),
|
||||
transform,
|
||||
});
|
||||
}
|
||||
@@ -204,7 +221,7 @@ impl NodeRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
async fn execute_network(&mut self, graph: NodeNetwork, render_config: RenderConfig) -> Result<TaggedValue, String> {
|
||||
async fn update_network(&mut self, graph: NodeNetwork) -> Result<(), String> {
|
||||
if self.editor_api.application_io.is_none() {
|
||||
self.editor_api = WasmEditorApi {
|
||||
application_io: Some(WasmApplicationIo::new().await.into()),
|
||||
@@ -215,44 +232,31 @@ impl NodeRuntime {
|
||||
.into();
|
||||
}
|
||||
|
||||
let editor_api = &self.editor_api;
|
||||
// Required to ensure that the appropriate proto nodes are reinserted when the Editor API changes.
|
||||
let mut graph_input_hash = DefaultHasher::new();
|
||||
editor_api.font_cache.hash(&mut graph_input_hash);
|
||||
let _font_hash_code = graph_input_hash.finish();
|
||||
graph.hash(&mut graph_input_hash);
|
||||
let hash_code = graph_input_hash.finish();
|
||||
let scoped_network = wrap_network_in_scope(graph, self.editor_api.clone());
|
||||
self.monitor_nodes = scoped_network
|
||||
.recursive_nodes()
|
||||
.filter(|(_, node)| node.implementation == DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_, _, _>"))
|
||||
.map(|(_, node)| node.original_location.path.clone().unwrap_or_default())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if self.graph_hash != Some(hash_code) {
|
||||
self.graph_hash = None;
|
||||
// We assume only one output
|
||||
assert_eq!(scoped_network.exports.len(), 1, "Graph with multiple outputs not yet handled");
|
||||
let c = Compiler {};
|
||||
let proto_network = match c.compile_single(scoped_network) {
|
||||
Ok(network) => network,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
assert_ne!(proto_network.nodes.len(), 0, "No proto nodes exist?");
|
||||
if let Err(e) = self.executor.update(proto_network).await {
|
||||
self.node_graph_errors = e;
|
||||
}
|
||||
self.resolved_types = self.executor.document_node_types();
|
||||
|
||||
if self.graph_hash.is_none() || self.recompile_graph {
|
||||
let scoped_network = wrap_network_in_scope(graph, self.editor_api.clone());
|
||||
|
||||
self.monitor_nodes = scoped_network
|
||||
.recursive_nodes()
|
||||
.filter(|(_, node)| node.implementation == DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_, _, _>"))
|
||||
.map(|(_, node)| node.original_location.path.clone().unwrap_or_default())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// We assume only one output
|
||||
assert_eq!(scoped_network.exports.len(), 1, "Graph with multiple outputs not yet handled");
|
||||
let c = Compiler {};
|
||||
let proto_network = match c.compile_single(scoped_network) {
|
||||
Ok(network) => network,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
assert_ne!(proto_network.nodes.len(), 0, "No proto nodes exist?");
|
||||
if let Err(e) = self.executor.update(proto_network).await {
|
||||
self.node_graph_errors = e;
|
||||
} else {
|
||||
self.graph_hash = Some(hash_code);
|
||||
}
|
||||
self.resolved_types = self.executor.document_node_types();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_network(&mut self, render_config: RenderConfig) -> Result<TaggedValue, String> {
|
||||
use graph_craft::graphene_compiler::Executor;
|
||||
|
||||
let result = match self.executor.input_type() {
|
||||
@@ -266,16 +270,6 @@ impl NodeRuntime {
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
// if let TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform: _ }) = result {
|
||||
// let old_id = self.canvas_cache.insert(path.to_vec(), surface_id);
|
||||
// if let Some(old_id) = old_id {
|
||||
// if old_id != surface_id {
|
||||
// if let Some(io) = self.wasm_io.as_ref() {
|
||||
// io.destroy_surface(old_id)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -369,42 +363,24 @@ impl NodeRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn introspect_node(path: &[NodeId]) -> Option<Arc<dyn std::any::Any>> {
|
||||
NODE_RUNTIME
|
||||
.try_with(|runtime| {
|
||||
let runtime = runtime.try_borrow();
|
||||
if let Ok(ref runtime) = runtime {
|
||||
if let Some(ref mut runtime) = runtime.as_ref() {
|
||||
return runtime.executor.introspect(path).flatten();
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.unwrap_or(None)
|
||||
pub async fn introspect_node(path: &[NodeId]) -> Option<Arc<dyn std::any::Any>> {
|
||||
let runtime = NODE_RUNTIME.lock().await;
|
||||
if let Some(ref mut runtime) = runtime.as_ref() {
|
||||
return runtime.executor.introspect(path).flatten();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn run_node_graph() {
|
||||
let result = NODE_RUNTIME.try_with(|runtime| {
|
||||
let runtime = runtime.clone();
|
||||
async move {
|
||||
let mut runtime = runtime.try_borrow_mut();
|
||||
if let Ok(ref mut runtime) = runtime {
|
||||
if let Some(ref mut runtime) = runtime.as_mut() {
|
||||
runtime.run().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if let Ok(result) = result {
|
||||
result.await;
|
||||
let mut runtime = NODE_RUNTIME.lock().await;
|
||||
if let Some(ref mut runtime) = runtime.as_mut() {
|
||||
runtime.run().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_node_runtime(runtime: NodeRuntime) -> Option<NodeRuntime> {
|
||||
NODE_RUNTIME.with(|node_runtime| {
|
||||
let mut node_runtime = node_runtime.borrow_mut();
|
||||
node_runtime.replace(runtime)
|
||||
})
|
||||
pub async fn replace_node_runtime(runtime: NodeRuntime) -> Option<NodeRuntime> {
|
||||
let mut node_runtime = NODE_RUNTIME.lock().await;
|
||||
node_runtime.replace(runtime)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -412,6 +388,7 @@ pub struct NodeGraphExecutor {
|
||||
sender: Sender<NodeRuntimeMessage>,
|
||||
receiver: Receiver<NodeGraphUpdate>,
|
||||
futures: HashMap<u64, ExecutionContext>,
|
||||
node_graph_hash: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -423,32 +400,29 @@ impl Default for NodeGraphExecutor {
|
||||
fn default() -> Self {
|
||||
let (request_sender, request_receiver) = std::sync::mpsc::channel();
|
||||
let (response_sender, response_receiver) = std::sync::mpsc::channel();
|
||||
replace_node_runtime(NodeRuntime::new(request_receiver, response_sender));
|
||||
futures::executor::block_on(replace_node_runtime(NodeRuntime::new(request_receiver, response_sender)));
|
||||
|
||||
Self {
|
||||
futures: Default::default(),
|
||||
sender: request_sender,
|
||||
receiver: response_receiver,
|
||||
node_graph_hash: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeGraphExecutor {
|
||||
/// Execute the network by flattening it and creating a borrow stack.
|
||||
fn queue_execution(&self, network: NodeNetwork, render_config: RenderConfig) -> u64 {
|
||||
fn queue_execution(&self, render_config: RenderConfig) -> u64 {
|
||||
let execution_id = generate_uuid();
|
||||
let request = ExecutionRequest {
|
||||
graph: network,
|
||||
execution_id,
|
||||
render_config,
|
||||
};
|
||||
let request = ExecutionRequest { execution_id, render_config };
|
||||
self.sender.send(NodeRuntimeMessage::ExecutionRequest(request)).expect("Failed to send generation request");
|
||||
|
||||
execution_id
|
||||
}
|
||||
|
||||
pub fn introspect_node(&self, path: &[NodeId]) -> Option<Arc<dyn std::any::Any>> {
|
||||
introspect_node(path)
|
||||
pub async fn introspect_node(&self, path: &[NodeId]) -> Option<Arc<dyn std::any::Any>> {
|
||||
introspect_node(path).await
|
||||
}
|
||||
|
||||
pub fn update_font_cache(&self, font_cache: FontCache) {
|
||||
@@ -473,7 +447,7 @@ impl NodeGraphExecutor {
|
||||
return None;
|
||||
};
|
||||
let introspection_node = find_node(wrapped_network)?;
|
||||
let introspection = self.introspect_node(&[node_path, &[introspection_node]].concat())?;
|
||||
let introspection = futures::executor::block_on(self.introspect_node(&[node_path, &[introspection_node]].concat()))?;
|
||||
let Some(downcasted): Option<&T> = <dyn std::any::Any>::downcast_ref(introspection.as_ref()) else {
|
||||
log::warn!("Failed to downcast type for introspection");
|
||||
return None;
|
||||
@@ -482,9 +456,13 @@ impl NodeGraphExecutor {
|
||||
}
|
||||
|
||||
/// Evaluates a node graph, computing the entire graph
|
||||
pub fn submit_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, viewport_resolution: UVec2) -> Result<(), String> {
|
||||
pub fn submit_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, viewport_resolution: UVec2, ignore_hash: bool) -> Result<(), String> {
|
||||
// Get the node graph layer
|
||||
let network = document.network().clone();
|
||||
let network_hash = document.network().current_hash();
|
||||
if network_hash != self.node_graph_hash || ignore_hash {
|
||||
self.node_graph_hash = network_hash;
|
||||
self.sender.send(NodeRuntimeMessage::GraphUpdate(document.network.clone())).map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
let render_config = RenderConfig {
|
||||
viewport: Footprint {
|
||||
@@ -502,7 +480,7 @@ impl NodeGraphExecutor {
|
||||
};
|
||||
|
||||
// Execute the node graph
|
||||
let execution_id = self.queue_execution(network, render_config);
|
||||
let execution_id = self.queue_execution(render_config);
|
||||
|
||||
self.futures.insert(execution_id, ExecutionContext { export_config: None });
|
||||
|
||||
@@ -537,7 +515,8 @@ impl NodeGraphExecutor {
|
||||
export_config.size = size;
|
||||
|
||||
// Execute the node graph
|
||||
let execution_id = self.queue_execution(network, render_config);
|
||||
self.sender.send(NodeRuntimeMessage::GraphUpdate(network)).map_err(|e| e.to_string())?;
|
||||
let execution_id = self.queue_execution(render_config);
|
||||
let execution_context = ExecutionContext { export_config: Some(export_config) };
|
||||
self.futures.insert(execution_id, execution_context);
|
||||
|
||||
@@ -585,13 +564,10 @@ impl NodeGraphExecutor {
|
||||
new_click_targets,
|
||||
new_vector_modify,
|
||||
new_upstream_transforms,
|
||||
resolved_types,
|
||||
node_graph_errors,
|
||||
transform,
|
||||
} = execution_response;
|
||||
|
||||
responses.extend(existing_responses.into_iter().map(Into::into));
|
||||
responses.add(NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors });
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
|
||||
@@ -616,6 +592,22 @@ impl NodeGraphExecutor {
|
||||
self.process_node_graph_output(node_graph_output, transform, responses)?
|
||||
}
|
||||
}
|
||||
NodeGraphUpdate::CompilationResponse(execution_response) => {
|
||||
let CompilationResponse {
|
||||
resolved_types,
|
||||
node_graph_errors,
|
||||
result,
|
||||
} = execution_response;
|
||||
if let Err(e) = result {
|
||||
// Clear the click targets while the graph is in an un-renderable state
|
||||
document.metadata.update_from_monitor(HashMap::new(), HashMap::new());
|
||||
log::trace!("{e}");
|
||||
|
||||
return Err("Node graph evaluation failed".to_string());
|
||||
};
|
||||
|
||||
responses.add(NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors });
|
||||
}
|
||||
NodeGraphUpdate::NodeGraphUpdateMessage(NodeGraphUpdateMessage::ImaginateStatusUpdate) => {
|
||||
responses.add(DocumentMessage::PropertiesPanel(PropertiesPanelMessage::Refresh));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user