You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Support for single-file host with shader compilation #659
To add to this, cubecl and cutile-rs also have single file support, but both are proc-macro based so can never support the "entirety of rustc".
Putting both CPU and GPU code in one crate also has some disadvantages. Like, how do you declare dependencies and keep CPU and GPU dependencies separate? Like it can be done today but it's usually super ugly. So it's not just an implementation issue, but also a design issue.
The best implementation of single crate I've seen so far is std::offload, at the expense of doing 3 separate rustc invocations. I'm personally interested in this approach and there may be a chance to get it's functionality general enough where it can be applied to rust-gpu as well. (But I likely won't have the time to implement it)
Putting both CPU and GPU code in one crate also has some disadvantages. Like, how do you declare dependencies and keep CPU and GPU dependencies separate? Like it can be done today but it's usually super ugly. So it's not just an implementation issue, but also a design issue.
In practise it might not be a big issue as most shaders are a few lines long without dependencies (at least with glsl and wglsl shaders). So it makes for an easy get started and later in the life of that project it can be separated into two crates.
The best implementation of single crate I've seen so far is std::offload, at the expense of doing 3 separate rustc invocations. I'm personally interested in this approach and there may be a chance to get it's functionality general enough where it can be applied to rust-gpu as well. (But I likely won't have the time to implement it)
The current implementation of std::offload does not seem to support different shaders (or any shaders given it's OpenMP) so there are a lot of different questions, like what happens with statics, are they accessible to both host and shader. Another issue is about imports that you mention earlier that might need to be behind #[cfg(target_arch = "spirv")]. But it's surely interesting to explore.
I'm reopening the issue as others might finding it useful. Feel free to close if appropriate.
most shaders are a few lines long without dependencies
You do need to at least depend on spirv-std, and you need some way to declare which version of spirv-std since that also selects the version of rust-gpu cargo-gpu will install for you. We do reexport spirv_std::glam so at least you won't need an explicit glam dependency.
You do need to at least depend on spirv-std, and you need some way to declare which version of spirv-std since that also selects the version of rust-gpu cargo-gpu will install for you. We do reexport spirv_std::glam so at least you won't need an explicit glam dependency.
I was thinking about this since your reply, and started considering a design. I haven't worked with rustc before so this is just speculating.
But the idea is:
Build spirv-std in a cache folder directly (ie. ~/.cache/cargo-gpu/spirv-std/<version-hash>/) and build it with cargo build --target spirv-unknown-spv1.5.
Inject that folder into the shader's build via RUSTC_WRAPPER, by appending --extern spirv_std=<cached rlib> and -L dependency=<cached deps> so Cargo.toml stays untouched.
Ofc I might be missing something obvious which invalidates the whole thing. But it can be tested on the current code independently before trying for single-file host.
That sounds reasonable. You'd be loosing all capabilities that cargo gives you, but you're already fine trading that away. The biggest problem IMO is splitting shader code from CPU code, as shader code must not depend on std. And how this interacts with structs shared between both.
It is most certainly possible to do, just use standard conditional compilation tricks. The biggest annoyance is compiling host-side stuff twice, for which you simply add a feature like build-shader and make cargo-gpu-install conditional on that.
Works quite well for me, standard idiomatic Rust, not sure how much else could be done to improve it beyond reducing build.rs-side boilerplate further (there is quite a bit right now).
CUDA is a different model where host and guest code is intentionally mixed, while with rust-gpu you're simply producing SPIR-V, and how you run it (or whether you run it at all) is up to you.
I think I prefer rust-gpu's approach, it makes it very clear where the boundary between CPU and GPU is and when you cross it is very explicit. Though I can also see the appeal of CUDA approach in its ease of use.
Here is a complete example where shader and its host side is combined into a single crate, including the build-shader feature optimization I mentioned. I'll be building more of these in the future and do not plan for splitting them.
Maybe worth having an example like this somewhere for people to mimic? I found existing examples, especially those in this repository, being quite involved and not very straightforward to follow.
For examples, I made minimal workspaces in rust-gpu-template a while ago and would add new examples and templates there. But I'm not sure if I'll have the time to make such a template rn.
Activity
LiveDuo commentedon Oct 4, 2026
The
cuda-rustcrate is seems to be one file because they use a custom CLI tool to buildcargo oxide run vecaddwhich arguably is worse.Firestar99 commentedon Oct 5, 2026
To add to this,
cubeclandcutile-rsalso have single file support, but both are proc-macro based so can never support the "entirety of rustc".Putting both CPU and GPU code in one crate also has some disadvantages. Like, how do you declare dependencies and keep CPU and GPU dependencies separate? Like it can be done today but it's usually super ugly. So it's not just an implementation issue, but also a design issue.
The best implementation of single crate I've seen so far is
std::offload, at the expense of doing 3 separate rustc invocations. I'm personally interested in this approach and there may be a chance to get it's functionality general enough where it can be applied to rust-gpu as well. (But I likely won't have the time to implement it)It's all tradeoffs, all the way down...
LiveDuo commentedon Oct 5, 2026
In practise it might not be a big issue as most shaders are a few lines long without dependencies (at least with glsl and wglsl shaders). So it makes for an easy get started and later in the life of that project it can be separated into two crates.
The current implementation of std::offload does not seem to support different shaders (or any shaders given it's OpenMP) so there are a lot of different questions, like what happens with statics, are they accessible to both host and shader. Another issue is about imports that you mention earlier that might need to be behind
#[cfg(target_arch = "spirv")]. But it's surely interesting to explore.I'm reopening the issue as others might finding it useful. Feel free to close if appropriate.
Firestar99 commentedon Oct 7, 2026
You do need to at least depend on
spirv-std, and you need some way to declare which version of spirv-std since that also selects the version of rust-gpu cargo-gpu will install for you. We do reexportspirv_std::glamso at least you won't need an explicit glam dependency.LiveDuo commentedon Oct 7, 2026
I was thinking about this since your reply, and started considering a design. I haven't worked with
rustcbefore so this is just speculating.But the idea is:
spirv-stdin a cache folder directly (ie.~/.cache/cargo-gpu/spirv-std/<version-hash>/) and build it withcargo build --target spirv-unknown-spv1.5.RUSTC_WRAPPER, by appending--extern spirv_std=<cached rlib>and-L dependency=<cached deps>soCargo.tomlstays untouched.Ofc I might be missing something obvious which invalidates the whole thing. But it can be tested on the current code independently before trying for single-file host.
Firestar99 commentedon Oct 7, 2026
That sounds reasonable. You'd be loosing all capabilities that cargo gives you, but you're already fine trading that away. The biggest problem IMO is splitting shader code from CPU code, as shader code must not depend on std. And how this interacts with structs shared between both.
nazar-pc commentedon Oct 10, 2026
It is most certainly possible to do, just use standard conditional compilation tricks. The biggest annoyance is compiling host-side stuff twice, for which you simply add a feature like
build-shaderand makecargo-gpu-installconditional on that.Works quite well for me, standard idiomatic Rust, not sure how much else could be done to improve it beyond reducing build.rs-side boilerplate further (there is quite a bit right now).
CUDA is a different model where host and guest code is intentionally mixed, while with rust-gpu you're simply producing SPIR-V, and how you run it (or whether you run it at all) is up to you.
I think I prefer rust-gpu's approach, it makes it very clear where the boundary between CPU and GPU is and when you cross it is very explicit. Though I can also see the appeal of CUDA approach in its ease of use.
Here is a complete example where shader and its host side is combined into a single crate, including the
build-shaderfeature optimization I mentioned. I'll be building more of these in the future and do not plan for splitting them.Maybe worth having an example like this somewhere for people to mimic? I found existing examples, especially those in this repository, being quite involved and not very straightforward to follow.
Firestar99 commentedon Oct 12, 2026
To complete the list, https://graphite.art/ has the
raster-nodescrate that is also compiled for both CPU and GPU.For examples, I made minimal workspaces in rust-gpu-template a while ago and would add new examples and templates there. But I'm not sure if I'll have the time to make such a template rn.