Skip to content

Commit

Permalink
Allow printing proto graph in graphite-cli (#2388)
Browse files Browse the repository at this point in the history
Allow disabling running of node network in graphite-cli

It seems that graphite-cli currently crashes on some GPUs which do not
support Vulkan. This commit improves the CLI, adding an option to
disable running the node network, which prevents the crash (but also
prevents any output from being shown).

Change default log levels

Restructure argument parsing
  • Loading branch information
bradrn authored and TrueDoctor committed Mar 8, 2025
1 parent 0a91dd2 commit 85fac63
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 66 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions node-graph/graphene-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ image = { workspace = true, default-features = false, features = [
"png",
] }

# Required dependencies
clap = { version = "4.5.31", features = ["cargo", "derive"] }

# Optional local dependencies
wgpu-executor = { path = "../wgpu-executor", optional = true }
gpu-executor = { path = "../gpu-executor", optional = true }
Expand Down
174 changes: 108 additions & 66 deletions node-graph/graphene-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use graph_craft::document::*;
use graph_craft::graphene_compiler::{Compiler, Executor};
use graph_craft::proto::ProtoNetwork;
use graph_craft::util::load_network;
use graph_craft::wasm_application_io::EditorPreferences;
use graphene_core::application_io::{ApplicationIo, NodeGraphUpdateSender};
use graphene_core::text::FontCache;
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
use interpreted_executor::dynamic_executor::DynamicExecutor;

use clap::{Args, Parser, Subcommand};
use fern::colors::{Color, ColoredLevelConfig};
use futures::executor::block_on;
use interpreted_executor::util::wrap_network_in_scope;
use std::{error::Error, sync::Arc};
use std::{error::Error, path::PathBuf, sync::Arc};

struct UpdateLogger {}

Expand All @@ -20,57 +22,134 @@ impl NodeGraphUpdateSender for UpdateLogger {
}
}

#[derive(Debug, Parser)]
#[clap(name = "graphene-cli", version)]
pub struct App {
#[clap(flatten)]
global_opts: GlobalOpts,

#[clap(subcommand)]
command: Command,
}

#[derive(Debug, Subcommand)]
enum Command {
/// Help message for compile.
Compile {
/// Print proto network
#[clap(long, short = 'p')]
print_proto: bool,

/// Path to the .graphite document
document: PathBuf,
},
/// Help message for run.
Run {
/// Path to the .graphite document
document: PathBuf,

/// Path to the .graphite document
image: Option<PathBuf>,

/// Run the document in a loop. This is useful for spawning and maintaining a window
#[clap(long, short = 'l')]
run_loop: bool,
},
}

#[derive(Debug, Args)]
struct GlobalOpts {
/// Verbosity level (can be specified multiple times)
#[clap(long, short, global = true, action = clap::ArgAction::Count)]
verbose: u8,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
init_logging();
let app = App::parse();

let log_level = app.global_opts.verbose;

let document_path = std::env::args().nth(1).expect("No document path provided");
init_logging(log_level);

let image_path = std::env::args().nth(2);
let document_path = match app.command {
Command::Compile { ref document, .. } => document,
Command::Run { ref document, .. } => document,
};

let document_string = std::fs::read_to_string(&document_path).expect("Failed to read document");
let document_string = std::fs::read_to_string(document_path).expect("Failed to read document");

println!("creating gpu context",);
log::info!("creating gpu context",);
let mut application_io = block_on(WasmApplicationIo::new());
if let Some(image_path) = image_path {

if let Command::Run { image: Some(ref image_path), .. } = app.command {
application_io.resources.insert("null".to_string(), Arc::from(std::fs::read(image_path).expect("Failed to read image")));
}

let device = application_io.gpu_executor().unwrap().context.device.clone();
std::thread::spawn(move || loop {
std::thread::sleep(std::time::Duration::from_nanos(10));
device.poll(wgpu::Maintain::Poll);
});

let preferences = EditorPreferences {
use_vello: true,
..Default::default()
};
let editor_api = Arc::new(WasmEditorApi {
font_cache: FontCache::default(),
application_io: Some(application_io.into()),
node_graph_message_sender: Box::new(UpdateLogger {}),
editor_preferences: Box::new(EditorPreferences::default()),
editor_preferences: Box::new(preferences),
});

let executor = create_executor(document_string, editor_api)?;
let render_config = graphene_core::application_io::RenderConfig::default();
let proto_graph = compile_graph(document_string, editor_api)?;

loop {
let _result = (&executor).execute(render_config).await?;
std::thread::sleep(std::time::Duration::from_millis(16));
match app.command {
Command::Compile { print_proto, .. } => {
if print_proto {
println!("{}", proto_graph);
}
}
Command::Run { run_loop, .. } => {
std::thread::spawn(move || loop {
std::thread::sleep(std::time::Duration::from_nanos(10));
device.poll(wgpu::Maintain::Poll);
});
let executor = create_executor(proto_graph)?;
let render_config = graphene_core::application_io::RenderConfig::default();

loop {
let result = (&executor).execute(render_config).await?;
if !run_loop {
println!("{:?}", result);
break;
}
std::thread::sleep(std::time::Duration::from_millis(16));
}
}
}

Ok(())
}

fn init_logging() {
fn init_logging(log_level: u8) {
let default_level = match log_level {
0 => log::LevelFilter::Error,
1 => log::LevelFilter::Info,
2 => log::LevelFilter::Debug,
_ => log::LevelFilter::Trace,
};
let colors = ColoredLevelConfig::new().debug(Color::Magenta).info(Color::Green).error(Color::Red);
fern::Dispatch::new()
.chain(std::io::stdout())
.level_for("iced", log::LevelFilter::Trace)
.level_for("wgpu", log::LevelFilter::Debug)
.level(log::LevelFilter::Trace)
.level_for("wgpu", log::LevelFilter::Error)
.level_for("naga", log::LevelFilter::Error)
.level_for("wgpu_hal", log::LevelFilter::Error)
.level_for("wgpu_core", log::LevelFilter::Error)
.level(default_level)
.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]"),
record.module_path().unwrap_or(""),
message
))
})
Expand Down Expand Up @@ -98,53 +177,16 @@ fn fix_nodes(network: &mut NodeNetwork) {
}
}
}

fn create_executor(document_string: String, editor_api: Arc<WasmEditorApi>) -> Result<DynamicExecutor, Box<dyn Error>> {
fn compile_graph(document_string: String, editor_api: Arc<WasmEditorApi>) -> Result<ProtoNetwork, Box<dyn Error>> {
let mut network = load_network(&document_string);
fix_nodes(&mut network);

let wrapped_network = wrap_network_in_scope(network.clone(), editor_api);
let compiler = Compiler {};
let protograph = compiler.compile_single(wrapped_network)?;
let executor = block_on(DynamicExecutor::new(protograph)).unwrap();
Ok(executor)
compiler.compile_single(wrapped_network).map_err(|x| x.into())
}

// #[cfg(test)]
// mod test {
// use super::*;

// #[tokio::test]
// #[cfg_attr(not(feature = "wayland"), ignore)]
// async fn grays_scale() {
// let document_string = include_str!("../test_files/gray.graphite");
// let executor = create_executor(document_string.to_string()).unwrap();
// let editor_api = WasmEditorApi {
// image_frame: None,
// font_cache: &FontCache::default(),
// application_io: &block_on(WasmApplicationIo::new()),
// node_graph_message_sender: &UpdateLogger {},
// editor_preferences: &EditorPreferences::default(),
// render_config: graphene_core::application_io::RenderConfig::default(),
// };
// let result = (&executor).execute(editor_api.clone()).await.unwrap();
// println!("result: {result:?}");
// }

// #[tokio::test]
// #[cfg_attr(not(feature = "wayland"), ignore)]
// async fn hue() {
// let document_string = include_str!("../test_files/hue.graphite");
// let executor = create_executor(document_string.to_string()).unwrap();
// let editor_api = WasmEditorApi {
// image_frame: None,
// font_cache: &FontCache::default(),
// application_io: &block_on(WasmApplicationIo::new()),
// node_graph_message_sender: &UpdateLogger {},
// editor_preferences: &EditorPreferences::default(),
// render_config: graphene_core::application_io::RenderConfig::default(),
// };
// let result = (&executor).execute(editor_api.clone()).await.unwrap();
// println!("result: {result:?}");
// }
// }
fn create_executor(proto_network: ProtoNetwork) -> Result<DynamicExecutor, Box<dyn Error>> {
let executor = block_on(DynamicExecutor::new(proto_network)).map_err(|errors| errors.iter().map(|e| format!("{e:?}")).reduce(|acc, e| format!("{acc}\n{e}")).unwrap_or_default())?;
Ok(executor)
}

0 comments on commit 85fac63

Please sign in to comment.