Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for setting config in shader's Cargo.toml #34

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

tombh
Copy link
Contributor

@tombh tombh commented Jan 5, 2025

Managed to find quite an elegant solution where serde_json is doing all the heavy lifting. We even have automatic type checking on the config set in the shader crate's Cargo.toml!

Values are set in the TOML section: [package.metadata.rust-gpu.*].
So now the priority, from least to most, is:

  • Workspace metadata
  • Shader crate's metadata
  • CLI args

Fixes #15

Notable changes:

  • All clap args for the build and install subcommands are now
    defined in the spirv-builder-cli crate. This makes passing all the
    config between cargo gpu and spirv-builder-cli trivial as serde
    now does all the heavy lifting of creating and reading the JSON
    arguments.
  • There is no longer a toml subcommand. The shader crate's Cargo.tomls
    are always read and their configs used as the base for CLI args to
    override.

TODO:

@tombh
Copy link
Contributor Author

tombh commented Jan 6, 2025

I'll have a look at those failing tests tomorrow.

@tombh tombh changed the title Support all spirv builder config Support for setting config in shader's Cargo.toml Jan 6, 2025
@tombh tombh force-pushed the support-all-spirv-builder-config branch from 0e871d0 to 1bcaef6 Compare January 6, 2025 11:17
@tmvkrpxl0
Copy link

Neat, I actually have usecase for this. I need to enable some shader features.

@schell
Copy link
Collaborator

schell commented Jan 6, 2025

Great work!

I'm still a bit confused about the target and shader_target
arguments, are they both needed?

They don't have to both exist, but they have to boil down to the same thing (if I'm not mistaken) - we have to be able to determine the target specification file to give to SpirvBuilder. I guess it's difficult because we want to present all of SpirvBuilder's options, but it has two methods of specifying the target specification - the old way of taking the spirv spec (eg spirv-unknown-vulkan1.2) and the new way where we give it a path to a specification file explicitly.

That said, I don't think we'll ever use anything but the explicit path-to-specification-file method.

@schell
Copy link
Collaborator

schell commented Jan 6, 2025

Neat, I actually have usecase for this. I need to enable some shader features.

Yes! This is why we're prioritizing this work :)

@tombh tombh force-pushed the support-all-spirv-builder-config branch 2 times, most recently from 21d6294 to 0b738cd Compare January 6, 2025 22:06
@tombh
Copy link
Contributor Author

tombh commented Jan 6, 2025

Thanks!

So let's see if we can come to a conclusion about the old/new way of defining the target.

  • The old way is just about providing a target name that matches an existing target JSON file in the rust-gpu repo. The downside to that is that it limits the user, they can't provide their own target.
  • The new way allows specifying an arbitrary path. However, I notice that we still unconditionally do target_spec_dir()?.join(format!("{}.json", self.build.shader_target)), which means that an end user still can't provide their own absolute path. So it's currently effectively the same as the old way?

Ideally we want a little automation? For example something like, if the user doesn't provide an absolute path then we assume that the string is the name of an existing target that rust-gpu already has. And if the user provides a path then we just use the file that it points to. I could still be misunderstanding something though?

Either way I think we'll want something like cargo gpu show targets that lists the available known target names?

BTW I've also just pushed a little change that attempts to end-to-end test the changes by altering the output_dir = line in the shader-crate-template's Cargo.toml. But I think it's struggling with sed not being cross platform. Here's the justfile snippet:

cargo install --path crates/cargo-gpu
cargo gpu install --shader-crate crates/shader-crate-template --auto-install-rust-toolchain
sed -i 's/^output-dir =.*/output-dir = \/tmp\/testshaders/' crates/shader-crate-template/Cargo.toml
cargo gpu build --shader-crate crates/shader-crate-template
ls -lah /tmp/test-shaders

It seems a bit hacky. Can anybody think of another way? Like is there another config that we can change and detect in the final built shader?

Edit: the end-to-test is actually surfacing a bug! I think it's to do with - and _ in argument names 🤔

@tombh tombh force-pushed the support-all-spirv-builder-config branch 8 times, most recently from 67bf4a5 to bcd23c2 Compare January 9, 2025 18:30
@schell
Copy link
Collaborator

schell commented Jan 9, 2025

About defining a target - the new way is similar to the old way, but we control the paths at shader crate compile time, as opposed to them being hard coded at SpirvBuilder compile time to point to the target dir of the crate being built. So - yes, you're right that the user cannot supply their own target specification file (at least not without placing a new one into the blessed path in the cache directory).

But I think it would be easy enough to create one more option, something like --target-specification-file that would take precedence, and if it's omitted we construct the path from the target string and the target_spec_dir. That would cover all our bases, even though I think a user-supplied spec file will be very rare.

@schell
Copy link
Collaborator

schell commented Jan 9, 2025

Either way I think we'll want something like cargo gpu show targets that lists the available known target names?

I think that's a fine idea, I'll create an issue for that and we can tackle it after this?

@tombh tombh force-pushed the support-all-spirv-builder-config branch 5 times, most recently from 4d9b238 to 6eee8b3 Compare January 10, 2025 02:53
@tombh tombh force-pushed the support-all-spirv-builder-config branch from 6eee8b3 to 743cb3d Compare January 10, 2025 03:02
@tombh
Copy link
Contributor Author

tombh commented Jan 10, 2025

Okay, for now I've just hidden the --target argument (because SpirvBuilder::new() still needs a target string, maybe it can just be "" for now?), so then we just have the --shader-target argument. And we can add the --target-specification-file another time. Though spirv-builder::SpirvBuilder still has both the old and new "*target" arguments, but that's another repo, so maybe that should be discussed up there.

From my side this looks ready to merge, or at least ready to give a good testing to. I still want to add the cargo gpu show capabilities/extensions commands. But they don't strictly need to be included in this PR.

@tombh tombh force-pushed the support-all-spirv-builder-config branch from 743cb3d to 7564036 Compare January 10, 2025 14:44
This is particularly useful when `--auto-install-rust-toolchain` is used
as the consent prompt isn't show and no indication at all of what's
happening was given.
@tombh tombh force-pushed the support-all-spirv-builder-config branch from 7564036 to cfdf4ed Compare January 10, 2025 14:47
@tombh
Copy link
Contributor Author

tombh commented Jan 10, 2025

I added the cargo gpu show capabilities command. I ended up just copy-pasting the spirv::Capability enum. It seems the only way to serialise an enum is with the strum crate's EnumIter derive macro, and that can't be added to external types. We could send a PR to https://github.com/gfx-rs/rspirv but it seems like it's not an active repo.

I also looked into a cargo gpu show extensions command, but there's no machine-readable source. So I think it's okay to just include the https://github.com/KhronosGroup/SPIRV-Registry link in the CLI help output.

@schell
Copy link
Collaborator

schell commented Jan 10, 2025

@tombh could we write a small wrapper over the enum just to provide our own (de)serialization? Would that be easier?

@tombh
Copy link
Contributor Author

tombh commented Jan 10, 2025

I'd love to! I'm not sure how though, do you have any ideas? spirv::Capabiltiies (the original type in the external crate) has #[derive(serde::serialize)] on it, so I feel like there should be a way. But I think the reality is just that serde can only serialize/deserialize specific values of an enum, not the the whole enum itself.

Another thing I tried was to type alias, making the external type internal, so type MyCapability = serde::Capability, which does work, but then derives can't be added to type aliases, namely, we need: #[derive(strum_macros::EnumIter)].

I'm very open to trying other ideas 🤓

@tombh
Copy link
Contributor Author

tombh commented Jan 12, 2025

Found another bug and pushed the fix. Copypasting the new commit's message:

Feature-gate `gfx-rs/rspirv` dependency

Because our `spirv-builder-cli` can modify itself to comply with various
versions of `spirv-builder` from `rust-gpu`, then we also need to match
the version of `spirv` that we use to get `spirv::Capabilities`.

Namely that `rust-gpu <= v0.9.0` uses `spirv = "0.2.0"` and that
`rust-gpu >= "v0.9.0"` uses `spirv = "0.3.0".

Another solution to this could be to re-export `spirv` from `spirv-builder`.
But I'm not sure that would avoid compiling the entirety of `spirv-builder`
(which depends on specific Rust nightly versions), and besides, it's not
trivial to retroactively add re-exports to already-published version of
`rust-gpu`.

@schell
Copy link
Collaborator

schell commented Jan 12, 2025

I'll take a crack at the spirv::Capabiltiies (de)serialization problem.

@schell
Copy link
Collaborator

schell commented Jan 12, 2025

I see what you mean - it's not that values need serialization, we just need to enumerate all values. This is a much easier problem to solve, so long as you don't think too hard ;) How about something like this?

/// Iterator over all `Capability` variants.
pub fn capability_variants_iter() -> impl Iterator<Item = spirv::Capability> {
    // Since spirv::Capability is repr(u32) we can iterate over
    // u32s until some maximum
    #[expect(clippy::as_conversions, reason = "We know all variants are repr(u32)")]
    let last_capability = spirv::Capability::CacheControlsINTEL as u32;
    (0..=last_capability).filter_map(spirv::Capability::from_u32)
}

@schell
Copy link
Collaborator

schell commented Jan 12, 2025

...and if we really need to be complete you could iterate (0..u32::MAX), but I think that's overkill.

@tombh tombh force-pushed the support-all-spirv-builder-config branch from 9414478 to e536de6 Compare January 13, 2025 00:47
@tombh
Copy link
Contributor Author

tombh commented Jan 13, 2025

Ohhhhh, that's really good 😏 Thank you!

Pushed ✨

@schell
Copy link
Collaborator

schell commented Jan 14, 2025

Is this all ready to go now?

@tombh
Copy link
Contributor Author

tombh commented Jan 14, 2025

From my perspective yes. There's a new bug I've found from working on #39, though it only happens in certain cases of changing the spirv-std version in the shader crate's Cargo.toml. I've almost got a fix, but I was thinking I'd just make a separate PR for it because it's part of the work to get #39 done.

@LegNeato
Copy link

LegNeato commented Jan 15, 2025

There is also https://docs.rs/strum/latest/strum/derive.EnumIter.html. We use strum in the rust-gpu examples.

@tombh
Copy link
Contributor Author

tombh commented Jan 15, 2025

Yes, that's what I was using, but @schell found a much simpler method 🤓

Copy link
Collaborator

@schell schell left a comment

Choose a reason for hiding this comment

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

Wow! This is amazing work. Great job 👍 .

I just had a nitpick or two, but it's no big deal. Feel free to merge when you're ready!

crates/cargo-gpu/src/build.rs Outdated Show resolved Hide resolved

// Move `/install/spirv_install` to `/install`
let spirv_install = cli_args_json
.pointer("/install/spirv_install")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wow! This is a really helpful function that I didn't know existed! Thanks! 🙇

// `clippy::exit` seems to be a false positive in `main()`.
// See: https://github.com/rust-lang/rust-clippy/issues/13518
#[expect(clippy::restriction, reason = "Our central place for safely exiting")]
std::process::exit(1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍 good catch.

# Dependencies for CPU code
[target.'cfg(not(target_arch = "spirv"))'.dependencies]
glam = { version = "0.29", features = ["std"] }

[package.metadata.rust-gpu.build]
# Where to output the compiled shader. Defaults to where `cargo gpu` is called from.
# TODO: Should it default to the root of the shader crate?
Copy link
Collaborator

Choose a reason for hiding this comment

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

It probably should default to the root of the shader crate, but we can keep that for later.

# Set shader crate's cargo features.
features = []
# The compile target.
# TODO: `cargo gpu show targets` for all available options.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this still a TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we have an issue for it #36

use spirv_0_2 as spirv;

#[cfg(any(feature = "spirv-builder-0_10", feature = "rspirv-latest"))]
use spirv_0_3 as spirv;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Clever use of features and deps! I didn't know you could do this.

tombh added 3 commits January 22, 2025 17:43
Values are set in the TOML section: `[package.metadata.rust-gpu.*]`.
So now the priority, from least to most, is:
* Workspace metadata
* Shader crate's metadata
* CLI args

Fixes Rust-GPU#15

Notable changes:
* All `clap` args for the `build` and `install` subcommands are now
  defined in the `spirv-builder-cli` crate. This makes passing all the
  config between `cargo gpu` and `spirv-builder-cli` trivial as `serde`
  now does all the heavy lifting of creating and reading the JSON
  arguments.
* There is no longer a `toml` subcommand. The shader crate's `Cargo.toml`s
  are always read and their configs used as the base for CLI args to
  override.
Because our `spirv-builder-cli` can modify itself to comply with various
versions of `spirv-builder` from `rust-gpu`, then we also need to match
the version of `spirv` that we use to get `spirv::Capabilities`.

Namely that `rust-gpu <= v0.9.0` uses `spirv = "0.2.0"` and that
`rust-gpu >= "v0.9.0"` uses `spirv = "0.3.0".

Another solution to this could be to re-export `spirv` from `spirv-builder`.
But I'm not sure that would avoid compiling the entirety of `spirv-builder`
(which depends on specific Rust nightly versions), and besides, it's not
trivial to retroactively add re-exports to already-published version of
`rust-gpu`.
@tombh tombh force-pushed the support-all-spirv-builder-config branch from e536de6 to 0347ae5 Compare January 22, 2025 20:44
@tombh
Copy link
Contributor Author

tombh commented Jan 22, 2025

Great, this is ready then. I'd merge it but I don't have push permissions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add more SpirvBuilder fields as cli arguments
4 participants