Update Tauri to v2 and execute only the node graph in native (#2362)

* Migrate tauri app to v2

* Move flake files to sub directory

* Remove unused plugins

* Backport some of the tauri code

* Implement async node graph execution

Only move node runtime to native code

* Always use gpu feature for tauri

* Fix serialization

* Add logging filters

* Enable native window rendering with vello

* Cleanup

* Remove unused editor instance

* Remove changes from vite config

* Remove warnings

* Remove unused files

* Fix most tests

* Cleanup

* Apply frontend lint

* Readd flake.nix

* Fix tests using --all-features

* Code review

* Enable all backends

* Fix monitor node downcast types

* Change debug log to a warning

* Disable shader passthrough

* Cleanup unused imports

* Remove warning

* Update project setup instructions

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Dennis Kobert
2025-04-14 11:43:15 +00:00
committed by GitHub
co-authored by Keavon Chambers
parent 29479f6e3e
commit 9b23c7e2db
39 changed files with 10429 additions and 2831 deletions
+14 -13
View File
@@ -10,37 +10,38 @@ edition = "2021"
rust-version = "1.79"
[features]
# by default Tauri runs in production mode
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
default = ["custom-protocol"]
# this feature is used for production builds where `devPath` points to the filesystem
# By default Tauri runs in production mode when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
default = ["custom-protocol", "gpu"]
# This feature is used for production builds where `devPath` points to the filesystem
# DO NOT remove this
custom-protocol = ["tauri/custom-protocol"]
gpu = ["graphite-editor/gpu"]
[dependencies]
# Local dependencies
graphite-editor = { path = "../../editor" }
graphite-editor = { path = "../../editor", features = [
"gpu",
"ron",
"vello",
"decouple-execution",
] }
# Workspace dependencies
serde_json = { workspace = true }
serde = { workspace = true }
axum = { workspace = true }
chrono = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
tokio = { workspace = true, features = ["macros", "rt"] }
ron = { workspace = true }
log = { workspace = true }
fern = { workspace = true }
futures = { workspace = true }
# Required dependencies
tauri = { version = "1.5", features = [
"api-all",
"devtools",
"linux-protocol-headers",
"wry",
] }
tauri = { version = "2", features = ["devtools", "wry"] }
tauri-plugin-shell = "2"
tauri-plugin-http = "2"
[build-dependencies]
# Required dependencies
tauri-build = { version = "1.2", features = [] }
tauri-build = { version = "2", features = [] }
@@ -0,0 +1,6 @@
{
"identifier": "desktop-capability",
"platforms": ["macOS", "windows", "linux"],
"windows": ["main"],
"permissions": ["http:default"]
}
@@ -0,0 +1,49 @@
{
"identifier": "migrated",
"description": "permissions that were migrated from v1",
"local": true,
"windows": ["main"],
"permissions": [
"core:default",
"core:window:allow-create",
"core:window:allow-center",
"core:window:allow-request-user-attention",
"core:window:allow-set-resizable",
"core:window:allow-set-maximizable",
"core:window:allow-set-minimizable",
"core:window:allow-set-closable",
"core:window:allow-set-title",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-minimize",
"core:window:allow-unminimize",
"core:window:allow-show",
"core:window:allow-hide",
"core:window:allow-close",
"core:window:allow-set-decorations",
"core:window:allow-set-always-on-top",
"core:window:allow-set-content-protected",
"core:window:allow-set-size",
"core:window:allow-set-min-size",
"core:window:allow-set-max-size",
"core:window:allow-set-position",
"core:window:allow-set-fullscreen",
"core:window:allow-set-focus",
"core:window:allow-set-icon",
"core:window:allow-set-skip-taskbar",
"core:window:allow-set-cursor-grab",
"core:window:allow-set-cursor-visible",
"core:window:allow-set-cursor-icon",
"core:window:allow-set-cursor-position",
"core:window:allow-set-ignore-cursor-events",
"core:window:allow-start-dragging",
"core:webview:allow-print",
"shell:allow-execute",
"shell:allow-open",
"http:default",
"core:app:allow-app-show",
"core:app:allow-app-hide",
"shell:default",
"http:default"
]
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"desktop-capability":{"identifier":"desktop-capability","description":"","local":true,"windows":["main"],"permissions":["http:default"],"platforms":["macOS","windows","linux"]},"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default","core:window:allow-create","core:window:allow-center","core:window:allow-request-user-attention","core:window:allow-set-resizable","core:window:allow-set-maximizable","core:window:allow-set-minimizable","core:window:allow-set-closable","core:window:allow-set-title","core:window:allow-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-set-always-on-top","core:window:allow-set-content-protected","core:window:allow-set-size","core:window:allow-set-min-size","core:window:allow-set-max-size","core:window:allow-set-position","core:window:allow-set-fullscreen","core:window:allow-set-focus","core:window:allow-set-icon","core:window:allow-set-skip-taskbar","core:window:allow-set-cursor-grab","core:window:allow-set-cursor-visible","core:window:allow-set-cursor-icon","core:window:allow-set-cursor-position","core:window:allow-set-ignore-cursor-events","core:window:allow-start-dragging","core:webview:allow-print","shell:allow-execute","shell:allow-open","http:default","core:app:allow-app-show","core:app:allow-app-hide","shell:default","http:default"]}}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+41 -57
View File
@@ -1,37 +1,16 @@
#![cfg_attr(all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows")]
use graphite_editor::application::Editor;
use graphite_editor::messages::prelude::*;
// use axum::body::StreamBody;
// use axum::extract::Path;
// use axum::http;
// use axum::response::IntoResponse;
use axum::routing::get;
use axum::Router;
use fern::colors::{Color, ColoredLevelConfig};
// use http::{Response, StatusCode};
use std::cell::RefCell;
// use std::collections::HashMap;
// use std::sync::Arc;
// use std::sync::Mutex;
use graphite_editor::application::Editor;
use graphite_editor::messages::prelude::*;
use graphite_editor::node_graph_executor::GraphRuntimeRequest;
use graphite_editor::node_graph_executor::NODE_RUNTIME;
use graphite_editor::node_graph_executor::*;
use std::sync::Mutex;
thread_local! {
static EDITOR: RefCell<Option<Editor>> = const { RefCell::new(None) };
}
// async fn respond_to(id: Path<String>) -> impl IntoResponse {
// let builder = Response::builder().header("Access-Control-Allow-Origin", "*").status(StatusCode::OK);
// let guard = IMAGES.lock().unwrap();
// let images = guard;
// let image = images.as_ref().unwrap().get(&id.0).unwrap();
// println!("image: {:#?}", image.path);
// let result: Result<Vec<u8>, &str> = Ok((*image.image_data).clone());
// let stream = futures::stream::once(async move { result });
// builder.body(StreamBody::new(stream)).unwrap()
// }
static NODE_RUNTIME_IO: Mutex<Option<NodeRuntimeIO>> = const { Mutex::new(None) };
#[tokio::main]
async fn main() {
@@ -42,63 +21,68 @@ async fn main() {
fern::Dispatch::new()
.chain(std::io::stdout())
.level(log::LevelFilter::Trace)
.level_for("naga", log::LevelFilter::Error)
.level_for("wgpu-hal", log::LevelFilter::Error)
.level_for("wgpu_hal", log::LevelFilter::Error)
.level_for("wgpu_core", log::LevelFilter::Error)
.format(move |out, message, record| {
out.finish(format_args!(
"[{}]{} {}",
"[{}]{} {} {}",
// This will color the log level only, not the whole line. Just a touch.
colors.color(record.level()),
chrono::Utc::now().format("[%Y-%m-%d %H:%M:%S]"),
message
message,
record.module_path().unwrap_or("")
))
})
.apply()
.unwrap();
// *(IMAGES.lock().unwrap()) = Some(HashMap::new());
std::thread::spawn(|| loop {
futures::executor::block_on(graphite_editor::node_graph_executor::run_node_graph());
std::thread::sleep(std::time::Duration::from_millis(16))
});
graphite_editor::application::set_uuid_seed(0);
EDITOR.with(|editor| editor.borrow_mut().replace(Editor::new()));
let app = Router::new().route("/", get(|| async { "Hello, World!" }))/*.route("/image/:id", get(respond_to))*/;
// run it with hyper on localhost:3000
let mut runtime_lock = NODE_RUNTIME_IO.lock().unwrap();
*runtime_lock = Some(NodeRuntimeIO::new());
drop(runtime_lock);
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
// Run it with hyper on localhost:3000
tauri::async_runtime::spawn(async {
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
});
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![set_random_seed, handle_message])
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![poll_node_graph, runtime_message])
.setup(|_app| {
use tauri::Manager;
_app.get_window("main").unwrap().open_devtools();
_app.get_webview_window("main").unwrap().open_devtools();
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
#[tauri::command]
fn set_random_seed(seed: f64) {
graphite_editor::application::set_uuid_seed(seed as u64);
fn poll_node_graph() -> String {
let vec: Vec<_> = NODE_RUNTIME_IO.lock().as_mut().unwrap().as_mut().unwrap().receive().collect();
ron::to_string(&vec).unwrap()
}
#[tauri::command]
fn handle_message(message: String) -> String {
let Ok(message) = ron::from_str::<graphite_editor::messages::message::Message>(&message) else {
panic!("Error parsing message: {message}")
};
let responses = EDITOR.with(|editor| {
let mut editor = editor.borrow_mut();
editor.as_mut().unwrap().handle_message(message)
});
for response in &responses {
let serialized = ron::to_string(&response.clone()).unwrap();
if let Err(error) = ron::from_str::<FrontendMessage>(&serialized) {
log::error!("Error deserializing message: {error}");
fn runtime_message(message: String) -> Result<(), String> {
let message = match ron::from_str(&message) {
Ok(message) => message,
Err(e) => {
log::error!("Failed to deserialize message: {}\nwith error: {}", message, e);
return Err("Failed to deserialize message".into());
}
}
// Process any `FrontendMessage` responses resulting from the backend processing the dispatched message
let result: Vec<_> = responses.into_iter().collect();
ron::to_string(&result).expect("Failed to serialize FrontendMessage")
};
let response = NODE_RUNTIME_IO.lock().as_ref().unwrap().as_ref().unwrap().send(message);
response
}
+42 -43
View File
@@ -1,60 +1,59 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": {
"beforeBuildCommand": "npm run tauri:build-wasm",
"beforeBuildCommand": "npm run tauri:build",
"beforeDevCommand": "npm run tauri:dev",
"distDir": "../dist",
"devPath": "http://127.0.0.1:8080/"
"frontendDist": "../dist",
"devUrl": "http://127.0.0.1:8080/"
},
"package": {
"productName": "Graphite",
"version": "0.1.0"
},
"tauri": {
"allowlist": {
"all": true
"bundle": {
"active": true,
"category": "DeveloperTool",
"copyright": "",
"targets": "all",
"externalBin": [],
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
},
"bundle": {
"active": true,
"category": "DeveloperTool",
"copyright": "",
"longDescription": "",
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"providerShortName": null,
"signingIdentity": null
},
"resources": [],
"shortDescription": "",
"linux": {
"deb": {
"depends": ["librustc_codegen_spirv"]
},
"externalBin": [],
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
"identifier": "rs.graphite.editor",
"longDescription": "",
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"providerShortName": null,
"signingIdentity": null
},
"resources": [],
"shortDescription": "",
"targets": "all",
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"security": {
"csp": null
},
"updater": {
"active": false
},
}
},
"productName": "Graphite",
"mainBinaryName": "Graphite",
"version": "0.1.0",
"identifier": "rs.graphite.editor",
"plugins": {},
"app": {
"withGlobalTauri": true,
"windows": [
{
"decorations": false,
"fullscreen": false,
"height": 600,
"height": 1080,
"resizable": true,
"title": "Graphite",
"width": 800
"width": 1920,
"useHttpsScheme": true
}
]
],
"security": {
"csp": null
}
}
}