-
Notifications
You must be signed in to change notification settings - Fork 12
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
WGSL preprocessor and modules #40
Comments
I don't think WGSL has any kind of official preprocessor. I don't have any strong opinions at the moment, but this feels like something that users should handle rather than There may need to be some API changes to make this easier for users to implement. I'd be interested in any feedback you may have when trying to use a preprocessor without modifying With the current
This sounds like each WGSL file before preprocessing would generate its own Rust module like In order for |
For now I'll write my preprocessed and generated files to
This is my biggest issue actually, because this also implies that there is only a single pipeline layout, which is not great for heterogeneous compute-heavy use-cases like the computer vision stuff I do. |
Is this related to #41? Is there a way you would like to structure your kernels, types, etc that isn't possible with wgsl_to_wgpu? |
My specific use-case is image processing with image preprocessing, keypoint detection, descriptor calculation, frame matching and pose estimation. This is basically a chain of operations where the types should be shared between the links, but you really don't want to bind all buffers in all steps. The problem is that Fortunately I haven't run into actual parallelization issues because of these barriers, but I probably will in the future. Oh, and I only use bind group 0 for compute kernels, I didn't want to deal with multiple groups. But I'm not really sure if that would solve the above problem. |
I've added a function that doesn't require an include path on the latest commit. This could also be part of the writer options, but it might not be worth a breaking change for people's build scripts. |
It's going to fit my use-case just fine, thanks. And yeah, breaking changes just for this sounds unnecessary. (If you ever plan on breaking compatibility, I suggest introducing a builder pattern for the options, or at least marking the options struct |
Bevy's syntax seems to be the most popular. It looks like they've switched to using naga-oil and working with https://bevyengine.org/news/bevy-0-11/#improved-shader-imports |
You could also use some very simple serializer (eg. bincode) on the |
I've been playing around with naga-oil and passing a In theory, we should be able to generate a Rust module for each WGSL "module". This would allow users to specify types like |
What seems more useful than a preprocessor is the ability to output only a subset of the Rust bindings So for example, if I have Here's a proof of concept where I just skip each struct/bindgroup that is also present in one of the imports: pub fn create_shader_module_with_imports(
wgsl_source: &str,
imported_sources: Vec<String>, // ex: code from other.wgsl
options: WriteOptions,
) -> Result<String, CreateModuleError> {
... Then if I run this once on |
It looks like the specific example you linked could be covered by pasting the contents of Checking the types in each imported module makes sense for preventing duplicates, but there's still the issue of how users will refer to the correct types. The
Is there a specific use case you had in mind with this? I think the current approach of accepting a processed WGSL source instead of a path handles most use cases I've encountered, but I realize that not every application is structured the same way. |
I'm using it to create a The problem with pasting I think you shouldn't end up with references to types in other modules unless you split the wgsl files weirdly |
Can you be more specific about the project you're working on? Someone mentioned above how they were having trouble with chaining operations for a computer vision workflow, for example. Does |
I'm just trying to set a bind group that is shared by all shaders in a render pass instead of creating identical bind groups for each shader |
That makes more sense now. Thanks. If all bind groups are shared, you can just define multiple entry points in the same WGSL file. This is technically still possible in a single file even if the bind groups aren't shared, but wgsl_to_wgpu doesn't support that yet because of #41. The workaround is to make a separate WGSL file for each entry point. If you only need to share uniform or storage buffers, you could just put the types in When dealing with this in my own code, I duplicate the types in each WGSL file but only create one bind group at runtime. This works but is less than ideal. |
Yeah I've been doing the same thing, just having duplicate files and then only using parts of the code generated by wgsl_to_wgpu. |
I've been thinking through how this would work in the simple case without any naming conflicts across modules. If we take a list of referenced modules as in your example, we can simply add a This could also be applied to bind groups, but it feels a bit weird to define some of the bind groups for an entry point in an external file. Bind groups are compatible if they have the same bind group layout. The proof of concept linked above also assumes that each pipeline has the same group index for the shared bind group. We would need some way to detect that the layouts are the same regardless of whether it's at |
To keep the API the same I'm using I agree that defining bind groups in a separate file is a bit weird. I have a single "global" bindgroup that is always group(0), and a second per-pass bindgroup that is always at group(1), and then each of my shaders start at group(2). I think just importing the structs still enables a setup like this though |
I made a fork mostly for my own use where I've added support for import syntax. For structs, I have it such that the generated rust modules hierarchy are the exactly same as you'd have in naga_oil's wgsl modules. But for bindings that might come from "other" wgsl files, I've decided to make it work more like a direct preprocessor import. (which in this case means, demangling naga oil's name and directly using the item name) Probably easier to show examples: The generator code you'd normally use in |
It doesn't look like it's feasible to use naga_oil at the moment without relying on internal implementation details related to module path name mangling. I reran my test code on the latest version, and it doesn't look like much has changed since #40 (comment). We probably only need to generate Rust code for types and constants in shared modules. The functions and bind groups can be handled by just parsing the final processed naga module from naga_oil. There may be edge cases where the entry function name gets mangled from imports. I'm open to adding this in the future if naga_oil provides a reversible way to demangle names or some other method to access the type information we need. |
I'm planning on introducing WGSL preprocessing to one of my projects, as complexity is quickly getting out of hand.
What I'm looking at right now is bevy's syntax, since it's supported by
wgsl analyzer
. It could be implemented as a separate crate, or, since it's such a simple piece of code, it could probably be added towgsl_to_wgpu
directly too. It could be interesting for types especially:#import
s could probably be turned intouse
declarations in the generated rust code in the future.Alternatives:
build.rs
step too. In this case, no modification towgsl_to_wgpu
is needed.I'd be happy to cobble together a PR, but I wanted to get some second opinions here first.
The text was updated successfully, but these errors were encountered: