Designing Vulkan Pipeline + Shader Layout Agreement

⚓ Rust    📅 2026-02-19    👤 surdeus    👁️ 7      

surdeus

This is a proc macro + build script problem. I think I have a solution. Sharing in case there are other ideas that deserve attempts.

Context

Pipelines are where layout agreement becomes fully decided. The Slang compiler can emit metadata about structs that are in use by a shader (for both Spir-V or MSL backends). When we compose pipeline stages, each stage must reference a shader source, allowing us to match all input layouts (uniforms, push constants, buffer types) to shader metadata.

Plan of Attack

To convert any mismatch into a compile time error, the following scheme is devised:

  1. build.rs invokes slang to emit metadata and evaluate hashes representing structure and field layouts
  2. rust structures use proc macros to emit const functions that evaluate to a hash of the structure layout
  3. pipeline stage hashes emit const functions that call the const functions of their fields, creating a composite hash of the entire stage
  4. the shader metadata must have evaluated to the same hashes or else an emitted const evaluation will fail.

Agreement between pipelines seems to be a matter of buffers, images, and their barriers, making it still an expression of pipeline stages rather than requiring more composition on top.

Since the const function only exists after macro expansion, any structure of structures will have to emit a const check that hopes that the other const check exists. Limitations like this are still compile time failures and not too confusing.

Even Better Ways to Feed RA?

The only way I can think of to emit span level errors is by emitting extra pre-checks to some file during builds, but they will get stale. Otherwise the macro expansions themselves cannot know what is going on with the child structs amirite?

Avoiding Generation

I decided not to do code generation because I think it will actually degrade the ergonomics, forcing us to import specific Rust types and complicating deduplication of types used in many shaders (hashes naturally deduplicate). We would have to wait on slangc to get updated Rust types instead of just typing fields.

For the first writing of a struct, I plan to include a macro that will create one Rust type from a slang shader source and type name. Expanding in the IDE can then "write" the annoying first copy while maintenance can use the const checks to enforce agreements stay contractually bound.

The downside of relying on such generation all the time would be again that Slang might re-use types and we only know the layouts from use in a shader.

The padding somewhat changes from type to type, and we can handle this by using different annotations to produe MyTypeUniform etc like other crates for handling this problem. If there is a disagreement, the user can combine the slang path & type in a macro call and generate the similar but slang-distinct types where necessary.

Recruiting Contributors

All of this design work is for MuTate and I people who want to gain experience or a useful technical artifact will find support for any useful work, including simple things like finishing the clap interface for the included DSP workbench.

1 post - 1 participant

Read full topic

🏷️ Rust_feed