Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 72 additions & 12 deletions Cargo.lock

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

7 changes: 5 additions & 2 deletions crates/rustc_codegen_spirv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ crate-type = ["dylib"]
default = ["use-compiled-tools"]
# If enabled, uses spirv-tools binaries installed in PATH, instead of
# compiling and linking the spirv-tools C++ code
use-installed-tools = ["spirv-tools/use-installed-tools"]
use-installed-tools = ["spirv-tools/use-installed-tools", "naga"]
# If enabled will compile and link the C++ code for the spirv tools, the compiled
# version is preferred if both this and `use-installed-tools` are enabled
use-compiled-tools = ["spirv-tools/use-compiled-tools"]
use-compiled-tools = ["spirv-tools/use-compiled-tools", "naga"]
Comment on lines +23 to +26
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, this effectively makes naga non-optional if I'm reading it correctly. Is it intentional?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's from the old PR, added with comment

wgsl: enable naga feature by default, cargo-gpu can't handle it

So this was more a hack than anything else. I'll have a look what the compile time impact is, and if it's actually significant (which next to spirv-tools-sys may not be), look into making it optional and supported by cargo-gpu.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if naga should really be a dependency of either of those two features. Why not let user specify it explicitly instead?

Copy link
Copy Markdown
Member Author

@Firestar99 Firestar99 Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cargo-gpu can't handle it

we'd need to build the infra in cargo-gpu first

# If enabled, this will not check whether the current rustc version is set to the
# appropriate channel. rustc_cogeden_spirv requires a specific nightly version,
# and will likely produce compile errors when built against a different toolchain.
# Enable this feature to be able to experiment with other versions.
skip-toolchain-check = []
naga = ["dep:naga"]

[dependencies]
# HACK(eddyb) these only exist to unify features across dependency trees,
Expand Down Expand Up @@ -61,6 +62,8 @@ itertools = "0.14.0"
tracing.workspace = true
tracing-subscriber.workspace = true
tracing-tree = "0.4.0"
naga = { version = "29.0.1", features = ["spv-in", "wgsl-out"], optional = true }
strum = { version = "0.27.2", features = ["derive"] }

[dev-dependencies]
pretty_assertions = "1.0"
Expand Down
1 change: 1 addition & 0 deletions crates/rustc_codegen_spirv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ mod custom_decorations;
mod custom_insts;
mod link;
mod linker;
mod naga_transpile;
mod spirv_type;
mod spirv_type_constraints;
mod symbols;
Expand Down
5 changes: 5 additions & 0 deletions crates/rustc_codegen_spirv/src/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::maybe_pqp_cg_ssa as rustc_codegen_ssa;

use crate::codegen_cx::{CodegenArgs, SpirvMetadata};
use crate::linker;
use crate::naga_transpile::should_transpile;
use crate::target::{SpirvTarget, SpirvTargetVariant};
use ar::{Archive, GnuBuilder, Header};
use rspirv::binary::Assemble;
Expand Down Expand Up @@ -319,6 +320,10 @@ fn post_link_single_module(

drop(save_modules_timer);
}

if let Ok(Some(transpile)) = should_transpile(sess) {
transpile(sess, cg_args, &spv_binary, out_filename).ok();
}
}

fn do_spirv_opt(
Expand Down
89 changes: 89 additions & 0 deletions crates/rustc_codegen_spirv/src/naga_transpile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::codegen_cx::CodegenArgs;
use crate::target::{NagaTarget, SpirvTarget};
use rustc_session::Session;
use rustc_span::ErrorGuaranteed;
use std::path::Path;

pub type NagaTranspile = fn(
sess: &Session,
cg_args: &CodegenArgs,
spv_binary: &[u32],
out_filename: &Path,
) -> Result<(), ErrorGuaranteed>;

pub fn should_transpile(sess: &Session) -> Result<Option<NagaTranspile>, ErrorGuaranteed> {
let target = SpirvTarget::parse_target(sess.opts.target_triple.tuple())
.expect("parsing should fail earlier");
let result: Result<Option<NagaTranspile>, ()> = match target {
#[cfg(feature = "naga")]
SpirvTarget::Naga(NagaTarget::NAGA_WGSL) => Ok(Some(transpile::wgsl_transpile)),
#[cfg(not(feature = "naga"))]
SpirvTarget::Naga(NagaTarget::NAGA_WGSL) => Err(()),
_ => Ok(None),
};
result.map_err(|_e| {
sess.dcx().err(format!(
"Target `{}` requires feature \"naga\" on rustc_codegen_spirv",
target.target()
))
})
}

#[cfg(feature = "naga")]
mod transpile {
use crate::codegen_cx::CodegenArgs;
use naga::error::ShaderError;
use naga::valid::Capabilities;
use rustc_session::Session;
use rustc_span::ErrorGuaranteed;
use std::path::Path;

pub fn wgsl_transpile(
sess: &Session,
_cg_args: &CodegenArgs,
spv_binary: &[u32],
out_filename: &Path,
) -> Result<(), ErrorGuaranteed> {
// these should be params via spirv-builder
let opts = naga::front::spv::Options::default();
let capabilities = Capabilities::all();
let writer_flags = naga::back::wgsl::WriterFlags::empty();

let module = naga::front::spv::parse_u8_slice(bytemuck::cast_slice(spv_binary), &opts)
.map_err(|err| {
sess.dcx().err(format!(
"Naga failed to parse spv: \n{}",
ShaderError {
source: String::new(),
label: None,
inner: Box::new(err),
}
))
})?;
let mut validator =
naga::valid::Validator::new(naga::valid::ValidationFlags::default(), capabilities);
let info = validator.validate(&module).map_err(|err| {
sess.dcx().err(format!(
"Naga validation failed: \n{}",
ShaderError {
source: String::new(),
label: None,
inner: Box::new(err),
}
))
})?;

let wgsl_dst = out_filename.with_extension("wgsl");
let wgsl = naga::back::wgsl::write_string(&module, &info, writer_flags).map_err(|err| {
sess.dcx()
.err(format!("Naga failed to write wgsl : \n{err}"))
})?;

std::fs::write(&wgsl_dst, wgsl).map_err(|err| {
sess.dcx()
.err(format!("failed to write wgsl to file: {err}"))
})?;

Ok(())
}
}
Loading
Loading