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:
Dennis Kobert
2024-07-15 15:14:48 +02:00
committed by GitHub
parent 59a943f42f
commit 212f08c6c8
66 changed files with 1572 additions and 1577 deletions

View File

@@ -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));
}