mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-24 08:05:04 +00:00
Allow printing proto graph in graphite-cli (#2388)
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
This commit is contained in:
parent
0a91dd2141
commit
85fac63bb2
3 changed files with 127 additions and 66 deletions
|
@ -1,5 +1,6 @@
|
|||
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};
|
||||
|
@ -7,10 +8,11 @@ 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 {}
|
||||
|
||||
|
@ -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 document_path = std::env::args().nth(1).expect("No document path provided");
|
||||
let log_level = app.global_opts.verbose;
|
||||
|
||||
let image_path = std::env::args().nth(2);
|
||||
init_logging(log_level);
|
||||
|
||||
let document_string = std::fs::read_to_string(&document_path).expect("Failed to read document");
|
||||
let document_path = match app.command {
|
||||
Command::Compile { ref document, .. } => document,
|
||||
Command::Run { ref document, .. } => document,
|
||||
};
|
||||
|
||||
println!("creating gpu context",);
|
||||
let document_string = std::fs::read_to_string(document_path).expect("Failed to read document");
|
||||
|
||||
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
|
||||
))
|
||||
})
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue