Skip to content

Latest commit

 

History

History
117 lines (84 loc) · 5.52 KB

File metadata and controls

117 lines (84 loc) · 5.52 KB

wgsl-rs

Rust-to-WGSL transpiler using proc macros. Workspace with wgsl-rs, wgsl-rs-ir, wgsl-rs-macros, example, and xtask crates.

The proc-macro converts a parsed Rust module to an owned IR (defined in wgsl-rs-ir), then emits a WGSL_MODULE static carrying a constructor that builds that IR at runtime. The runtime crate (wgsl-rs) renders the IR to WGSL on demand and supports IR-level type substitution for generic shader instantiation.

Project Lore

Historical data, insights, affirmations, etc.

The Two Worlds Problem

A key insight is that wgsl-rs maintains two parallel representations:

  1. Rust World: The code must compile as valid Rust (design decision #1 in DEVLOG.md) that runs on the CPU.
  2. WGSL World: The proc-macro transpiles to WGSL that runs on the GPU.

These are fundamentally different execution contexts with different memory models, and yet running a wgsl-rs program should produce roughly the same results in both "worlds".

Program setup (or preamble, if you will) and the runtime behavior is expected to be different for each world, but the results should match, within reason.

Commands

cargo build                                 # Build all crates
cargo test                                  # Run all tests
cargo test -p wgsl-rs-macros                # Test specific crate (wgsl-rs-macros in this case)
cargo test -- test_name                     # Run a single test by name
cargo fmt && cargo clippy                   # Format and lint (note: use `cargo +nightly fmt` — rustfmt.toml uses unstable features)
cargo run -p roundtrip-tests                # Run the round-trip tests to ensure the "two worlds" agree
cargo run -p example                        # Run the example, showing help text about subcommands
cargo run -p example -- show                # Show the names of available example modules
cargo run -p example -- source {name}       # Validate and print a single example module's WGSL source
cargo expand -p example -- examples::{name} # Expand the example which uses the `wgsl` macro, showing the generated WGSL_MODULE
cargo clippy --all-features                 # Show all clippy lints

Always remember to run cargo +nightly fmt after making changes (the rustfmt.toml uses unstable features that require the nightly toolchain; stable cargo fmt silently skips them and may leave formatting diffs).

xtask - development tools for agents

This repo contains a cargo xtask that provides agents with some shorthand commands for common development tasks.

wgsl-spec

cargo xtask wgsl-spec provides access to the WGSL specification without overwhelming an agent's context window.

cargo xtask wgsl-spec toc                        # List WGSL spec table of contents
cargo xtask wgsl-spec section <anchor>           # Fetch a spec section with subsections
cargo xtask wgsl-spec section --shallow <anchor> # Fetch section without subsections
cargo xtask wgsl-spec section <anchor> <sub>     # Fetch a specific subsection

CI actions

cargo xtask ci provides access to continuous integration actions.

Most importantly this includes a subcommand to run after making changes, before committing or pushing a PR:

cargo xtask ci --help          # Show CI subcommands
cargo xtask ci pr-check        # Run all checks before pushing a PR

Code Style

  • Imports: Standard lib and external crates first, then use crate:: for internal modules
  • Errors: Use snafu with span info for IDE integration (SomethingWrongSnafu { span, note }.fail()?)
  • Naming: PascalCase types, snake_case functions/modules, SCREAMING_SNAKE_CASE constants
  • Patterns: TryFrom for AST conversions, traits per WGSL builtin, macros for repetitive impls, and use unreachable! when encountering an invariant that is impossible. Similarly use .expect("{reason}") where appropriate.
  • Spans: Preserve proc_macro2::Span on all parsed types for error mapping back to Rust source
  • Comments: Prefer to split distinct concerns into separate modules with module-level documentation rather than reaching for // ===== Section ===== headers in a single file. That said, // ===== headers are acceptable inside long files (e.g. monomorphize.rs, parse.rs) where splitting into sub-modules would itself be a larger refactor than the organizational benefit warrants. Please document all functions. If parameters are self-explanatory, don't bother writing parameter docs.

Contributor AI Disclosure

All code contributions generated by AI, or in collaboration with AI must be disclosed.

When collaborating with generative AI, please author commits with the format:

{human-author} with {llm-name} {llm-version} <{human-email}>

This can be accomplished easily with two steps:

  1. A normal commit via git commit ...
  2. Amend the author with git commit --amend --author "{human-author} with {llm-name} {llm-version} <{human-email}>"

For more information, see NLnet's AI Disclosure Policy.

DEVLOG.md file

The DEVLOG is a set of long-lived development notes.

Most importantly, it contains design decisions made during development. These decisions answer why wgsl-rs is the way it is.

Each design decision should have an entry with the heading "### YYYY-MM-DD: {description}", followed by an explanation of the problem, the decision and justification, etc. Multiple decisions made on the same day should be separate entries — the date is metadata; what matters is that each entry captures one distinct decision.