mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 06:38:03 +08:00
Remove unsafe code and clean up the code base in general (#1263)
* Remove unsafe code * Make node graph test syncronous * Add miri step to ci * Remove unsafe from node graph evaluation * Replace operation pseudo_hash with hash based on discriminant * Fix test * Move memo module to core and make it safe * Fix formatting * Remove unused stuff from gstd * Use safe casting for creating key variants * Fix memo node types * Fix ref node * "fix" ub * Use correct input types for ExtractImageFrame * Fix types for async nodes * Fix missing implementation * Manually override output type for async nodes * Fix types for EditorApi * Fix output type for WasmSurfaceHandle * Remove unused miri.yml * Fix incorrect type for cache node
This commit is contained in:
committed by
Keavon Chambers
parent
259dcdc628
commit
4e1bfddcd8
@@ -12,10 +12,7 @@ license = "Apache-2.0"
|
||||
|
||||
[features]
|
||||
gpu = ["interpreted-executor/gpu", "graphene-std/gpu", "graphene-core/gpu"]
|
||||
quantization = [
|
||||
"graphene-std/quantization",
|
||||
"interpreted-executor/quantization",
|
||||
]
|
||||
quantization = ["graphene-std/quantization", "interpreted-executor/quantization"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
@@ -41,10 +38,10 @@ image = { version = "0.24", default-features = false, features = [
|
||||
] }
|
||||
graph-craft = { path = "../node-graph/graph-craft" }
|
||||
interpreted-executor = { path = "../node-graph/interpreted-executor" }
|
||||
borrow_stack = { path = "../node-graph/borrow_stack" }
|
||||
dyn-any = { path = "../libraries/dyn-any" }
|
||||
graphene-core = { path = "../node-graph/gcore" }
|
||||
graphene-std = { path = "../node-graph/gstd" }
|
||||
num_enum = "0.6.1"
|
||||
|
||||
[dependencies.document-legacy]
|
||||
path = "../document-legacy"
|
||||
|
||||
@@ -323,6 +323,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
/// - create rect, shape and ellipse
|
||||
/// - select shape
|
||||
/// - copy
|
||||
@@ -362,6 +363,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn copy_paste_folder() {
|
||||
let mut editor = create_editor_with_three_layers();
|
||||
|
||||
@@ -450,6 +452,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
/// - create rect, shape and ellipse
|
||||
/// - select ellipse and rect
|
||||
/// - copy
|
||||
|
||||
@@ -37,9 +37,9 @@ impl InputMapperMessageHandler {
|
||||
.filter_map(|(i, m)| {
|
||||
let ma = m.0.iter().find_map(|m| actions.find_map(|a| (a == m.action.to_discriminant()).then(|| m.action.to_discriminant())));
|
||||
|
||||
ma.map(|a| unsafe { (std::mem::transmute_copy::<usize, Key>(&i), a) })
|
||||
ma.map(|a| ((i as u8).try_into().unwrap(), a))
|
||||
})
|
||||
.for_each(|(k, a)| {
|
||||
.for_each(|(k, a): (Key, _)| {
|
||||
let _ = write!(output, "{}: {}, ", k.to_discriminant().local_name(), a.local_name().split('.').last().unwrap());
|
||||
});
|
||||
output.replace("Key", "")
|
||||
@@ -72,7 +72,7 @@ impl InputMapperMessageHandler {
|
||||
"Attempting to convert a Key with enum index {}, which is larger than the number of Key enums",
|
||||
i
|
||||
);
|
||||
unsafe { std::mem::transmute_copy::<usize, Key>(&i) }
|
||||
(i as u8).try_into().unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
||||
@@ -50,7 +50,8 @@ bitflags! {
|
||||
// (although we ignore the shift key, so the user doesn't have to press `Ctrl Shift +` on a US keyboard), even if the keyboard layout
|
||||
// is for a different locale where the `+` key is somewhere entirely different, shifted or not. This would then also work for numpad `+`.
|
||||
#[impl_message(Message, InputMapperMessage, KeyDown)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, specta::Type)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, specta::Type, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum Key {
|
||||
// Writing system keys
|
||||
Digit0,
|
||||
|
||||
@@ -41,28 +41,6 @@ pub enum Message {
|
||||
Workspace(WorkspaceMessage),
|
||||
}
|
||||
|
||||
impl Message {
|
||||
/// Returns the byte representation of the message.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function reads from uninitialized memory!!!
|
||||
/// Only use if you know what you are doing.
|
||||
unsafe fn as_slice(&self) -> &[u8] {
|
||||
core::slice::from_raw_parts(self as *const Message as *const u8, std::mem::size_of::<Message>())
|
||||
}
|
||||
|
||||
/// Returns a pseudo hash that should uniquely identify the message.
|
||||
/// This is needed because `Hash` is not implemented for `f64`s
|
||||
///
|
||||
/// # Safety
|
||||
/// This function reads from uninitialized memory but the generated value should be fine.
|
||||
pub fn pseudo_hash(&self) -> u64 {
|
||||
let mut s = DefaultHasher::new();
|
||||
unsafe { self.as_slice() }.hash(&mut s);
|
||||
s.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides an impl of `specta::Type` for `MessageDiscriminant`, the struct created by `impl_message`.
|
||||
/// Specta isn't integrated with `impl_message`, so a remote impl must be provided using this
|
||||
/// struct.
|
||||
|
||||
@@ -1001,6 +1001,10 @@ impl DocumentMessageHandler {
|
||||
// Calculate the size of the region to be exported and generate an SVG of the artwork below this layer within that region
|
||||
let transform = self.document_legacy.multiply_transforms(&layer_path).unwrap();
|
||||
let size = DVec2::new(transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length());
|
||||
// TODO: Fix this hack
|
||||
// This is a hack to prevent the compiler from optimizing out the size calculation which likely is due
|
||||
// to undefined behavior. THIS IS NOT A FIX.
|
||||
log::trace!("size: {:?}", size);
|
||||
let svg = self.render_document(size, transform.inverse(), persistent_data, DocumentRenderMode::OnlyBelowLayerInFolder(&layer_path));
|
||||
|
||||
self.restore_document_transform(old_transforms);
|
||||
|
||||
@@ -148,7 +148,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||
1,
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::node(0, 0)],
|
||||
implementation: DocumentNodeImplementation::proto("graphene_std::memo::MonitorNode<_>"),
|
||||
implementation: DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_>"),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
@@ -198,7 +198,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||
DocumentNode {
|
||||
name: "Cache".to_string(),
|
||||
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::node(0, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
|
||||
..Default::default()
|
||||
},
|
||||
// We currently just clone by default
|
||||
@@ -262,7 +262,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||
DocumentNode {
|
||||
name: "Cache".to_string(),
|
||||
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::node(0, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
|
||||
..Default::default()
|
||||
},
|
||||
]
|
||||
@@ -305,7 +305,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||
DocumentNode {
|
||||
name: "Cache".to_string(),
|
||||
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::node(1, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
@@ -355,13 +355,13 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||
DocumentNode {
|
||||
name: "LetNode".to_string(),
|
||||
inputs: vec![NodeInput::node(0, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::LetNode<_>")),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::LetNode<_>")),
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
name: "RefNode".to_string(),
|
||||
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::lambda(1, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::RefNode<_, _>")),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::RefNode<_, _>")),
|
||||
..Default::default()
|
||||
},
|
||||
]
|
||||
@@ -392,7 +392,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||
DocumentNodeType {
|
||||
name: "End Scope",
|
||||
category: "Ignore",
|
||||
identifier: NodeImplementation::proto("graphene_std::memo::EndLetNode<_>"),
|
||||
identifier: NodeImplementation::proto("graphene_core::memo::EndLetNode<_>"),
|
||||
inputs: vec![
|
||||
DocumentInputType {
|
||||
name: "Scope",
|
||||
@@ -666,47 +666,6 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||
],
|
||||
properties: node_properties::no_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Gaussian Blur",
|
||||
category: "Ignore",
|
||||
identifier: NodeImplementation::DocumentNode(NodeNetwork {
|
||||
inputs: vec![0, 1, 1],
|
||||
outputs: vec![NodeOutput::new(1, 0)],
|
||||
nodes: vec![
|
||||
(
|
||||
0,
|
||||
DocumentNode {
|
||||
name: "CacheNode".to_string(),
|
||||
inputs: vec![NodeInput::Network(concrete!(Image<Color>))],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "BlurNode".to_string(),
|
||||
inputs: vec![NodeInput::node(0, 0), NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(f64)), NodeInput::node(0, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::raster::BlurNode")),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
..Default::default()
|
||||
}),
|
||||
inputs: vec![
|
||||
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||
DocumentInputType::value("Radius", TaggedValue::U32(3), false),
|
||||
DocumentInputType::value("Sigma", TaggedValue::F64(1.), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType {
|
||||
name: "Image",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
}],
|
||||
properties: node_properties::blur_image_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Brush",
|
||||
category: "Brush",
|
||||
@@ -735,34 +694,9 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||
properties: node_properties::no_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Cache",
|
||||
name: "Memoize",
|
||||
category: "Structural",
|
||||
identifier: NodeImplementation::DocumentNode(NodeNetwork {
|
||||
inputs: vec![0],
|
||||
outputs: vec![NodeOutput::new(1, 0)],
|
||||
nodes: [
|
||||
(
|
||||
0,
|
||||
DocumentNode {
|
||||
name: "CacheNode".to_string(),
|
||||
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::Network(concrete!(ImageFrame<Color>))],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "CloneNode".to_string(),
|
||||
inputs: vec![NodeInput::node(0, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::CloneNode<_>")),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
]
|
||||
.into(),
|
||||
..Default::default()
|
||||
}),
|
||||
identifier: NodeImplementation::proto("graphene_core::memo::MemoNode<_, _>"),
|
||||
inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true)],
|
||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||
properties: node_properties::no_properties,
|
||||
@@ -778,7 +712,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||
DocumentNodeType {
|
||||
name: "Ref",
|
||||
category: "Structural",
|
||||
identifier: NodeImplementation::proto("graphene_std::memo::CacheNode"),
|
||||
identifier: NodeImplementation::proto("graphene_core::memo::MemoNode<_, _>"),
|
||||
inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true)],
|
||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||
properties: node_properties::no_properties,
|
||||
@@ -1330,7 +1264,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork {
|
||||
let mut network_inputs = Vec::new();
|
||||
let mut input_type = None;
|
||||
for (id, node) in network.nodes.iter() {
|
||||
for (index, input) in node.inputs.iter().enumerate() {
|
||||
for input in node.inputs.iter() {
|
||||
if let NodeInput::Network(_) = input {
|
||||
if input_type.is_none() {
|
||||
input_type = Some(input.clone());
|
||||
|
||||
@@ -1222,9 +1222,7 @@ fn edit_layer_deepest_manipulation(intersect: &Layer, responses: &mut VecDeque<M
|
||||
fn recursive_search(document: &DocumentMessageHandler, layer_path: &Vec<u64>, incoming_layer_path_vector: &Vec<u64>) -> bool {
|
||||
let layer_paths = document.document_legacy.folder_children_paths(layer_path);
|
||||
for path in layer_paths {
|
||||
if path == *incoming_layer_path_vector {
|
||||
return true;
|
||||
} else if document.document_legacy.is_folder(path.clone()) && recursive_search(document, &path, incoming_layer_path_vector) {
|
||||
if path == *incoming_layer_path_vector || document.document_legacy.is_folder(path.clone()) && recursive_search(document, &path, incoming_layer_path_vector) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use crate::messages::prelude::*;
|
||||
use document_legacy::layers::layer_info::LayerDataType;
|
||||
use document_legacy::{LayerId, Operation};
|
||||
|
||||
use dyn_any::DynAny;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeNetwork};
|
||||
use graph_craft::executor::Compiler;
|
||||
@@ -18,7 +17,7 @@ use graphene_core::renderer::{SvgSegment, SvgSegmentList};
|
||||
use graphene_core::text::FontCache;
|
||||
use graphene_core::vector::style::ViewMode;
|
||||
|
||||
use graphene_core::wasm_application_io::{WasmApplicationIo, WasmSurfaceHandleFrame};
|
||||
use graphene_core::wasm_application_io::WasmApplicationIo;
|
||||
use graphene_core::{Color, EditorApi, SurfaceFrame, SurfaceId};
|
||||
use interpreted_executor::executor::DynamicExecutor;
|
||||
|
||||
@@ -110,7 +109,7 @@ impl NodeRuntime {
|
||||
updates: responses,
|
||||
new_thumbnails: self.thumbnails.clone(),
|
||||
};
|
||||
self.sender.send(response);
|
||||
self.sender.send(response).expect("Failed to send response");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,7 +117,7 @@ impl NodeRuntime {
|
||||
|
||||
/// Wraps a network in a scope and returns the new network and the paths to the monitor nodes.
|
||||
fn wrap_network(network: NodeNetwork) -> (NodeNetwork, Vec<Vec<NodeId>>) {
|
||||
let mut scoped_network = wrap_network_in_scope(network);
|
||||
let scoped_network = wrap_network_in_scope(network);
|
||||
|
||||
//scoped_network.generate_node_paths(&[]);
|
||||
let monitor_nodes = scoped_network
|
||||
@@ -126,7 +125,6 @@ impl NodeRuntime {
|
||||
.filter(|(node, _, _)| node.implementation == DocumentNodeImplementation::proto("graphene_std::memo::MonitorNode<_>"))
|
||||
.map(|(_, _, path)| path)
|
||||
.collect();
|
||||
//scoped_network.remove_dead_nodes();
|
||||
|
||||
(scoped_network, monitor_nodes)
|
||||
}
|
||||
@@ -149,40 +147,23 @@ impl NodeRuntime {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
use dyn_any::IntoDynAny;
|
||||
use graph_craft::executor::Executor;
|
||||
|
||||
let result = match self.executor.input_type() {
|
||||
Some(t) if t == concrete!(EditorApi) => self.executor.execute(editor_api.into_dyn()).await.map_err(|e| e.to_string()),
|
||||
Some(t) if t == concrete!(()) => self.executor.execute(().into_dyn()).await.map_err(|e| e.to_string()),
|
||||
Some(t) if t == concrete!(EditorApi) => (&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()),
|
||||
};
|
||||
}?;
|
||||
|
||||
match result {
|
||||
Ok(result) => {
|
||||
if DynAny::type_id(result.as_ref()) == core::any::TypeId::of::<WasmSurfaceHandleFrame>() {
|
||||
let Ok(value) = dyn_any::downcast::<WasmSurfaceHandleFrame>(result) else { unreachable!()};
|
||||
let new_id = value.surface_handle.surface_id;
|
||||
let old_id = self.canvas_cache.insert(path.to_vec(), new_id);
|
||||
if let Some(old_id) = old_id {
|
||||
if old_id != new_id {
|
||||
self.wasm_io.destroy_surface(old_id);
|
||||
}
|
||||
}
|
||||
return Ok(TaggedValue::SurfaceFrame(SurfaceFrame {
|
||||
surface_id: new_id,
|
||||
transform: value.transform,
|
||||
}));
|
||||
}
|
||||
|
||||
let type_name = DynAny::type_name(result.as_ref());
|
||||
match TaggedValue::try_from_any(result) {
|
||||
Some(x) => Ok(x),
|
||||
None => Err(format!("Invalid output type: {}", type_name)),
|
||||
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 {
|
||||
self.wasm_io.destroy_surface(old_id);
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Recomputes the thumbnails for the layers in the graph, modifying the state and updating the UI.
|
||||
@@ -289,13 +270,13 @@ impl NodeGraphExecutor {
|
||||
image_frame,
|
||||
generation_id,
|
||||
};
|
||||
self.sender.send(NodeRuntimeMessage::GenerationRequest(request));
|
||||
self.sender.send(NodeRuntimeMessage::GenerationRequest(request)).expect("Failed to send generation request");
|
||||
|
||||
generation_id
|
||||
}
|
||||
|
||||
pub fn update_font_cache(&self, font_cache: FontCache) {
|
||||
self.sender.send(NodeRuntimeMessage::FontCacheUpdate(font_cache));
|
||||
self.sender.send(NodeRuntimeMessage::FontCacheUpdate(font_cache)).expect("Failed to send font cache update");
|
||||
}
|
||||
|
||||
pub fn introspect_node(&self, path: &[NodeId]) -> Option<Arc<dyn std::any::Any>> {
|
||||
|
||||
Reference in New Issue
Block a user