mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Lay groundwork for directly rendering to the canvas without a cpu roundrip (#1291)
* Add Texture handle type * Add Texture View to shader inputs * Implement basic rendering pipeline * Render first texture using render pipeline * Fix output color space * Precompute the rendering pipeline * Move gpu context creation to editor api * Port gpu-executor nodes to node registry * Fix canvas nodes and make code compile for non wasm targets * Pin wasm-bindgen version * Disable miri temoporarily for better ci times * Fix formatting * Remove unsafe block * Bump wasm-pack version * Bump wasm-bindgen version * Add gpu feature guard for push node * Make Into node async
This commit is contained in:
committed by
Keavon Chambers
parent
0c93a62d55
commit
45b04f4eb9
@@ -17,8 +17,9 @@ use graphene_core::renderer::{SvgSegment, SvgSegmentList};
|
||||
use graphene_core::text::FontCache;
|
||||
use graphene_core::vector::style::ViewMode;
|
||||
|
||||
use graphene_core::wasm_application_io::WasmApplicationIo;
|
||||
use graphene_core::{Color, EditorApi, SurfaceFrame, SurfaceId};
|
||||
use graphene_core::{Color, SurfaceFrame, SurfaceId};
|
||||
use graphene_std::wasm_application_io::WasmApplicationIo;
|
||||
use graphene_std::wasm_application_io::WasmEditorApi;
|
||||
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -33,7 +34,7 @@ pub struct NodeRuntime {
|
||||
font_cache: FontCache,
|
||||
receiver: Receiver<NodeRuntimeMessage>,
|
||||
sender: Sender<GenerationResponse>,
|
||||
wasm_io: WasmApplicationIo,
|
||||
wasm_io: Option<WasmApplicationIo>,
|
||||
pub(crate) thumbnails: HashMap<LayerId, HashMap<NodeId, SvgSegmentList>>,
|
||||
canvas_cache: HashMap<Vec<LayerId>, SurfaceId>,
|
||||
}
|
||||
@@ -69,7 +70,7 @@ impl NodeRuntime {
|
||||
sender,
|
||||
font_cache: FontCache::default(),
|
||||
thumbnails: Default::default(),
|
||||
wasm_io: WasmApplicationIo::default(),
|
||||
wasm_io: None,
|
||||
canvas_cache: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -125,10 +126,14 @@ impl NodeRuntime {
|
||||
}
|
||||
|
||||
async fn execute_network<'a>(&'a mut self, path: &[LayerId], scoped_network: NodeNetwork, image_frame: Option<ImageFrame<Color>>) -> Result<TaggedValue, String> {
|
||||
let editor_api = EditorApi {
|
||||
if self.wasm_io.is_none() {
|
||||
self.wasm_io = Some(WasmApplicationIo::new().await);
|
||||
}
|
||||
|
||||
let editor_api = WasmEditorApi {
|
||||
font_cache: &self.font_cache,
|
||||
image_frame,
|
||||
application_io: &self.wasm_io,
|
||||
application_io: &self.wasm_io.as_ref().unwrap(),
|
||||
};
|
||||
|
||||
// We assume only one output
|
||||
@@ -145,7 +150,7 @@ impl NodeRuntime {
|
||||
use graph_craft::graphene_compiler::Executor;
|
||||
|
||||
let result = match self.executor.input_type() {
|
||||
Some(t) if t == concrete!(EditorApi) => (&self.executor).execute(editor_api).await.map_err(|e| e.to_string()),
|
||||
Some(t) if t == concrete!(WasmEditorApi) => (&self.executor).execute(editor_api).await.map_err(|e| e.to_string()),
|
||||
Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()),
|
||||
_ => Err("Invalid input type".to_string()),
|
||||
}?;
|
||||
@@ -154,7 +159,7 @@ impl NodeRuntime {
|
||||
let old_id = self.canvas_cache.insert(path.to_vec(), surface_id);
|
||||
if let Some(old_id) = old_id {
|
||||
if old_id != surface_id {
|
||||
self.wasm_io.destroy_surface(old_id);
|
||||
self.wasm_io.as_ref().map(|io| io.destroy_surface(old_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,49 +298,6 @@ impl NodeGraphExecutor {
|
||||
self.last_output_type.get(path).cloned().flatten()
|
||||
}
|
||||
|
||||
/// Computes an input for a node in the graph
|
||||
pub fn compute_input<T: dyn_any::StaticType>(&mut self, _old_network: &NodeNetwork, _node_path: &[NodeId], _input_index: usize, _editor_api: Cow<EditorApi<'_>>) -> Result<u64, String> {
|
||||
todo!()
|
||||
/*
|
||||
let mut network = old_network.clone();
|
||||
// Adjust the output of the graph so we find the relevant output
|
||||
'outer: for end in (0..node_path.len()).rev() {
|
||||
let mut inner_network = &mut network;
|
||||
for &node_id in &node_path[..end] {
|
||||
inner_network.outputs[0] = NodeOutput::new(node_id, 0);
|
||||
|
||||
let Some(new_inner) = inner_network.nodes.get_mut(&node_id).and_then(|node| node.implementation.get_network_mut()) else {
|
||||
return Err("Failed to find network".to_string());
|
||||
};
|
||||
inner_network = new_inner;
|
||||
}
|
||||
match &inner_network.nodes.get(&node_path[end]).unwrap().inputs[input_index] {
|
||||
// If the input is from a parent network then adjust the input index and continue iteration
|
||||
NodeInput::Network(_) => {
|
||||
input_index = inner_network
|
||||
.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_index, &id)| id == node_path[end])
|
||||
.nth(input_index)
|
||||
.ok_or_else(|| "Invalid network input".to_string())?
|
||||
.0;
|
||||
}
|
||||
// If the input is just a value, return that value
|
||||
NodeInput::Value { tagged_value, .. } => return Some(dyn_any::downcast::<T>(tagged_value.clone().to_any()).map(|v| *v)),
|
||||
// If the input is from a node, set the node to be the output (so that is what is evaluated)
|
||||
NodeInput::Node { node_id, output_index, .. } => {
|
||||
inner_network.outputs[0] = NodeOutput::new(*node_id, *output_index);
|
||||
break 'outer;
|
||||
}
|
||||
NodeInput::ShortCircut(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
self.queue_execution(network, editor_api.into_owned())?
|
||||
*/
|
||||
}
|
||||
|
||||
/// Encodes an image into a format using the image crate
|
||||
fn encode_img(image: Image<Color>, resize: Option<DVec2>, format: image::ImageOutputFormat) -> Result<(Vec<u8>, (u32, u32)), String> {
|
||||
use image::{ImageBuffer, Rgba};
|
||||
|
||||
Reference in New Issue
Block a user