Skip to content

Commit e039e6d

Browse files
committed
Add --watch arg for auto re-compiling on shader changes
1 parent 0347ae5 commit e039e6d

File tree

5 files changed

+106
-77
lines changed

5 files changed

+106
-77
lines changed

crates/cargo-gpu/src/build.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! `cargo gpu build`, analogous to `cargo build`
22
33
use anyhow::Context as _;
4+
use std::io::Write as _;
45

56
use crate::{install::Install, target_spec_dir};
67
use spirv_builder_cli::{args::BuildArgs, Linkage, ShaderModule};
@@ -42,10 +43,12 @@ impl Build {
4243
std::env::current_dir()?.display()
4344
);
4445

45-
self.build_args.shader_target = target_spec_dir()?
46-
.join(format!("{}.json", self.build_args.shader_target))
47-
.display()
48-
.to_string();
46+
if !self.build_args.watch {
47+
self.build_args.shader_target = target_spec_dir()?
48+
.join(format!("{}.json", self.build_args.shader_target))
49+
.display()
50+
.to_string();
51+
}
4952

5053
let args_as_json = serde_json::json!({
5154
"install": self.install.spirv_install,
@@ -54,10 +57,12 @@ impl Build {
5457
let arg = serde_json::to_string_pretty(&args_as_json)?;
5558
log::info!("using spirv-builder-cli arg: {arg}");
5659

57-
crate::user_output!(
58-
"Running `spirv-builder-cli` to compile shader at {}...\n",
59-
self.install.spirv_install.shader_crate.display()
60-
);
60+
if !self.build_args.watch {
61+
crate::user_output!(
62+
"Running `spirv-builder-cli` to compile shader at {}...\n",
63+
self.install.spirv_install.shader_crate.display()
64+
);
65+
}
6166

6267
// Call spirv-builder-cli to compile the shaders.
6368
let output = std::process::Command::new(spirv_builder_cli_path)

crates/cargo-gpu/src/main.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,16 @@ fn run() -> anyhow::Result<()> {
141141
let mut command =
142142
config::Config::clap_command_with_cargo_config(&shader_crate_path, env_args)?;
143143
log::debug!("building with final merged arguments: {command:#?}");
144-
command.run()?;
144+
145+
if command.build_args.watch {
146+
// When watching, do one normal run to setup the `manifest.json` file.
147+
command.build_args.watch = false;
148+
command.run()?;
149+
command.build_args.watch = true;
150+
command.run()?;
151+
} else {
152+
command.run()?;
153+
}
145154
}
146155
Command::Show(show) => show.run()?,
147156
Command::DumpUsage => dump_full_usage_for_readme()?,

crates/spirv-builder-cli/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ optional = true
4040

4141
[dependencies.spirv-builder-pre-cli]
4242
package = "spirv-builder"
43+
features = [ "watch" ]
4344
optional = true
4445
git = "https://github.com/Rust-GPU/rust-gpu" # ${AUTO-REPLACE-SOURCE}
4546
rev = "4c633aec" # ${AUTO-REPLACE-VERSION}
4647

4748
[dependencies.spirv-builder-0_10]
4849
package = "spirv-builder"
50+
features = [ "watch" ]
4951
optional = true
5052
git = "https://github.com/Rust-GPU/rust-gpu" # ${AUTO-REPLACE-SOURCE}
5153
rev = "60dcb82" # ${AUTO-REPLACE-VERSION}

crates/spirv-builder-cli/src/args.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use spirv_0_3 as spirv;
66

77
use std::str::FromStr as _;
88

9-
#[derive(clap::Parser, Debug, serde::Deserialize, serde::Serialize)]
9+
#[derive(clap::Parser, Debug, Clone, serde::Deserialize, serde::Serialize)]
1010
pub struct AllArgs {
1111
#[clap(flatten)]
1212
pub build: BuildArgs,
@@ -26,12 +26,16 @@ pub enum SpirvMetadata {
2626
Full,
2727
}
2828

29-
#[derive(clap::Parser, Debug, serde::Deserialize, serde::Serialize)]
29+
#[derive(clap::Parser, Debug, Clone, serde::Deserialize, serde::Serialize)]
3030
pub struct BuildArgs {
3131
/// Path to the output directory for the compiled shaders.
3232
#[clap(long, short, default_value = "./")]
3333
pub output_dir: std::path::PathBuf,
3434

35+
/// Watch the shader crate directory and automatically recompile on changes.
36+
#[clap(long, short, action)]
37+
pub watch: bool,
38+
3539
/// Set shader crate's cargo default-features.
3640
#[clap(long)]
3741
pub no_default_features: bool,
@@ -133,7 +137,7 @@ impl BuildArgs {
133137
}
134138
}
135139

136-
#[derive(clap::Parser, Debug, serde::Deserialize, serde::Serialize)]
140+
#[derive(clap::Parser, Debug, Clone, serde::Deserialize, serde::Serialize)]
137141
pub struct InstallArgs {
138142
#[clap(long, hide(true), default_value = "INTERNALLY_SET")]
139143
pub dylib_path: std::path::PathBuf,

crates/spirv-builder-cli/src/main.rs

Lines changed: 74 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,32 @@ fn set_codegen_spirv_location(dylib_path: std::path::PathBuf) {
4242
std::env::set_var(env_var, path);
4343
}
4444

45+
fn handle_compile_result(result: &CompileResult, args: &args::AllArgs) {
46+
log::debug!("found entry points: {:#?}", result.entry_points);
47+
48+
let dir = &args.build.output_dir;
49+
let mut shaders = vec![];
50+
match &result.module {
51+
ModuleResult::MultiModule(modules) => {
52+
assert!(!modules.is_empty(), "No shader modules to compile");
53+
for (entry, filepath) in modules.clone().into_iter() {
54+
log::debug!("compiled {entry} {}", filepath.display());
55+
shaders.push(ShaderModule::new(entry, filepath));
56+
}
57+
}
58+
ModuleResult::SingleModule(filepath) => {
59+
for entry in result.entry_points.clone() {
60+
shaders.push(ShaderModule::new(entry, filepath.clone()));
61+
}
62+
}
63+
}
64+
65+
use std::io::Write;
66+
let mut file = std::fs::File::create(dir.join("spirv-manifest.json")).unwrap();
67+
file.write_all(&serde_json::to_vec(&shaders).unwrap())
68+
.unwrap();
69+
}
70+
4571
pub fn main() {
4672
env_logger::builder().init();
4773

@@ -54,84 +80,67 @@ pub fn main() {
5480
);
5581
log::debug!("with args: {args:#?}");
5682
let args: args::AllArgs = serde_json::from_str(&args[1]).unwrap();
83+
let args_for_result = args.clone();
5784

5885
let spirv_metadata = match args.build.spirv_metadata {
5986
args::SpirvMetadata::None => spirv_builder::SpirvMetadata::None,
6087
args::SpirvMetadata::NameVariables => spirv_builder::SpirvMetadata::NameVariables,
6188
args::SpirvMetadata::Full => spirv_builder::SpirvMetadata::Full,
6289
};
6390

64-
let CompileResult {
65-
entry_points,
66-
module,
67-
} = {
68-
let mut builder = SpirvBuilder::new(args.install.shader_crate, &args.build.target)
69-
.deny_warnings(args.build.deny_warnings)
70-
.release(!args.build.debug)
71-
.multimodule(args.build.multimodule)
72-
.spirv_metadata(spirv_metadata)
73-
.relax_struct_store(args.build.relax_struct_store)
74-
.relax_logical_pointer(args.build.relax_logical_pointer)
75-
.relax_block_layout(args.build.relax_block_layout)
76-
.uniform_buffer_standard_layout(args.build.uniform_buffer_standard_layout)
77-
.scalar_block_layout(args.build.scalar_block_layout)
78-
.skip_block_layout(args.build.skip_block_layout)
79-
.preserve_bindings(args.build.preserve_bindings)
80-
.print_metadata(spirv_builder::MetadataPrintout::None);
81-
82-
for capability in &args.build.capability {
83-
builder = builder.capability(*capability);
84-
}
85-
86-
for extension in &args.build.extension {
87-
builder = builder.extension(extension);
88-
}
89-
90-
#[cfg(feature = "spirv-builder-pre-cli")]
91-
{
92-
set_codegen_spirv_location(args.install.dylib_path);
93-
}
91+
let mut builder = SpirvBuilder::new(args.install.shader_crate, &args.build.target)
92+
.deny_warnings(args.build.deny_warnings)
93+
.release(!args.build.debug)
94+
.multimodule(args.build.multimodule)
95+
.spirv_metadata(spirv_metadata)
96+
.relax_struct_store(args.build.relax_struct_store)
97+
.relax_logical_pointer(args.build.relax_logical_pointer)
98+
.relax_block_layout(args.build.relax_block_layout)
99+
.uniform_buffer_standard_layout(args.build.uniform_buffer_standard_layout)
100+
.scalar_block_layout(args.build.scalar_block_layout)
101+
.skip_block_layout(args.build.skip_block_layout)
102+
.preserve_bindings(args.build.preserve_bindings)
103+
.print_metadata(spirv_builder::MetadataPrintout::None);
104+
105+
for capability in &args.build.capability {
106+
builder = builder.capability(*capability);
107+
}
94108

95-
#[cfg(feature = "spirv-builder-0_10")]
96-
{
97-
builder = builder
98-
.rustc_codegen_spirv_location(args.install.dylib_path)
99-
.target_spec(args.build.shader_target);
109+
for extension in &args.build.extension {
110+
builder = builder.extension(extension);
111+
}
100112

101-
if args.build.no_default_features {
102-
log::info!("setting cargo --no-default-features");
103-
builder = builder.shader_crate_default_features(false);
104-
}
105-
if !args.build.features.is_empty() {
106-
log::info!("setting --features {:?}", args.build.features);
107-
builder = builder.shader_crate_features(args.build.features);
108-
}
109-
}
113+
#[cfg(feature = "spirv-builder-pre-cli")]
114+
{
115+
set_codegen_spirv_location(args.install.dylib_path);
116+
}
110117

111-
log::debug!("Calling `rust-gpu`'s `spirv-builder` library");
112-
builder.build().unwrap()
113-
};
114-
log::debug!("found entry points: {entry_points:#?}");
118+
#[cfg(feature = "spirv-builder-0_10")]
119+
{
120+
builder = builder
121+
.rustc_codegen_spirv_location(args.install.dylib_path)
122+
.target_spec(args.build.shader_target);
115123

116-
let dir = args.build.output_dir;
117-
let mut shaders = vec![];
118-
match module {
119-
ModuleResult::MultiModule(modules) => {
120-
assert!(!modules.is_empty(), "No shader modules to compile");
121-
for (entry, filepath) in modules.into_iter() {
122-
log::debug!("compiled {entry} {}", filepath.display());
123-
shaders.push(ShaderModule::new(entry, filepath));
124-
}
124+
if args.build.no_default_features {
125+
log::info!("setting cargo --no-default-features");
126+
builder = builder.shader_crate_default_features(false);
125127
}
126-
ModuleResult::SingleModule(filepath) => {
127-
for entry in entry_points {
128-
shaders.push(ShaderModule::new(entry, filepath.clone()));
129-
}
128+
if !args.build.features.is_empty() {
129+
log::info!("setting --features {:?}", args.build.features);
130+
builder = builder.shader_crate_features(args.build.features);
130131
}
131132
}
132133

133-
use std::io::Write;
134-
let mut file = std::fs::File::create(dir.join("spirv-manifest.json")).unwrap();
135-
file.write_all(&serde_json::to_vec(&shaders).unwrap())
136-
.unwrap();
134+
log::debug!("Calling `rust-gpu`'s `spirv-builder` library");
135+
136+
if args.build.watch {
137+
println!("🦀 Watching and recompiling shader on changes...");
138+
builder.watch(move |compile_result| {
139+
handle_compile_result(&compile_result, &args_for_result);
140+
});
141+
std::thread::park();
142+
} else {
143+
let result = builder.build().unwrap();
144+
handle_compile_result(&result, &args_for_result);
145+
}
137146
}

0 commit comments

Comments
 (0)