1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
//! # wgslender
//!
//! WGSL shader tooling for Rust: [`minify`], [`validate`], [`lint`],
//! [`reflect`], [`compile`] to a binary shader module, and [`refactor`] by
//! stable symbol id — the whole [wgslender] C library behind one safe API.
//!
//! ## Quick start
//!
// Without the macros there is nothing to expand, so the run-time API *is* the
// quick start and needs its own lead-in.
//!
//! ```
//! let source = "\
//! @group(0) @binding(0) var<storage, read_write> data: array<f32>;
//!
//! @compute @workgroup_size(64)
//! fn main(@builtin(global_invocation_id) id: vec3u) {
//! data[id.x] = data[id.x] * 2.0;
//! }
//! ";
//!
//! // A shader the library accepts …
//! let checked = wgslender::validate(source, wgslender::Strictness::Default)?;
//! assert!(checked.valid);
//!
//! // … shrinks, while the binding a host program binds against keeps its name.
//! let minified = wgslender::minify(source)?;
//! assert!(minified.len() < source.len());
//! assert!(minified.contains("data"));
//!
//! // What the pipeline needs is in the reflection, not in the text.
//! let reflection = wgslender::reflect(source)?;
//! assert_eq!(reflection.bindings[0].name, "data");
//! assert_eq!(reflection.entry_points[0].workgroup_size, Some([64, 1, 1]));
//! # Ok::<(), wgslender::Error>(())
//! ```
//!
//! ## What is here
//!
//! | To | Call | Run |
//! |---|---|---|
//! | shrink a shader | [`minify`], [`minify_with`], [`minify_and_reflect`] | `minify`, `minify_options` |
//! | check one | [`validate`], [`lint`], [`lint_fix`] | `validate`, `lint` |
//! | describe one | [`reflect`], [`reflect_json`] | `reflect_types` |
//! | embed one in the binary | `include_wgsl!`, or `include_wgsl_compressed!` to store it deflated | `include_wgsl`, `embed_compressed` |
//! | generate Rust from one | `wgsl_module!` | `wgsl_module` |
//! | embed one as WebAssembly | [`compile`] | `compile` |
//! | edit one by symbol | [`refactor`] | `refactor` |
//!
//! The last column is a runnable example, one `cargo run --example minify` per
//! name — `embed_compressed` wants `--features compress`, the rest build with
//! the defaults. There is no row without one, and the repository's gate runs
//! all ten, so a name in that column is a program that worked this morning
//! rather than a snippet that once did.
//!
//! ## Compile-time embedding
//!
//! `include_wgsl!` does the validating and the minifying while `cargo build`
//! runs, so a shader with a mistake in it fails the build rather than the
//! pipeline, and the bytes in the binary are the small ones. Its own page has
//! the worked example, the option table, and the one surprising rule: paths are
//! relative to the crate root, not to the file the macro is written in.
//!
//! `include_wgsl_compressed!`, behind the `compress` feature, goes one step
//! further: the binary carries a deflate stream, and a `CompressedWgsl`
//! inflates it on first use — once, or never, if nothing ever creates the
//! pipeline. On this crate's own demo fixture that is 968 bytes of source
//! stored as 274. `cargo run --features compress --example embed_compressed`
//! prints the three numbers.
//!
//! ## Generated Rust
//!
//! `wgsl_module!` goes further still: it reflects the shader at compile time
//! and generates the module a host program binds against — the slot of every
//! resource, and a `#[repr(C)]` struct per buffer with the shader's padding
//! made explicit, so the whole thing can be written to the GPU as bytes. Each
//! struct carries a `const _: ()` proof of its own size, alignment and field
//! offsets, which means a mis-mapping is a failed build rather than a wrong
//! pixel. `cargo run --example wgsl_module` prints one.
//!
//! ## What counts as an error
//!
//! A shader the library rejects is not a Rust `Err`. [`validate`] answers
//! `Ok(Validation)` whose `valid` is false, and [`lint`] an `Ok(LintReport)`
//! full of diagnostics — the shader failing *is* the answer. [`Error`] is for
//! the call itself going wrong, plus the two deliberate exceptions
//! [`Error::Compile`] and [`Error::Refactor`], both documented on the variant.
//!
//! ## Cargo features
//!
//! | Feature | Default | What it adds |
//! |---|---|---|
//! | `macros` | on | [`include_wgsl!`](include_wgsl) and [`wgsl_module!`](wgsl_module), and with them a proc-macro dependency that runs the library at compile time. |
//! | `compress` | off | `CompressedWgsl`, and — if `macros` is also on — `include_wgsl_compressed!`. Pulls in `miniz_oxide` for DEFLATE. |
//!
//! Turning `macros` off (`default-features = false`) leaves every function
//! above intact; it only removes the macros and the crates behind them.
//!
//! ## Edition support
//!
//! Edition 2024, so consumers need rustc 1.85 or newer. Building also runs
//! `zig build lib` against the wgslender sources, which needs Zig 0.16.0 —
//! unless `WGSLENDER_LIB_DIR` points at a prebuilt `libwgslender.a`. See
//! `packages/rust/README.md` in the repository for both paths.
//!
//! ## Crates
//!
//! This is the crate to depend on. [`wgslender-core`](wgslender_core) holds the
//! implementation, `wgslender-sys` the raw FFI declarations and
//! `wgslender-macros` the proc-macro; they are named separately so that the
//! `unsafe` and the proc-macro each have somewhere to live, not so that anyone
//! has to reach for them.
//!
//! [wgslender]: https://github.com/HugoDaniel/wgslender
/// Everything the implementation crate makes public, under this crate's name.
///
/// A glob rather than a written-out list, deliberately: the facade exists to
/// give the API a stable name, and re-exporting item by item would let the two
/// surfaces drift. It carries [`wgslender_core::refactor`] along with the flat
/// items, so that module reads as `wgslender::refactor`.
pub use *;
/// Embed a WGSL file, validated and minified at compile time.
///
/// Named here rather than written here: the macro has to live in a crate of its
/// own, because a `proc-macro = true` crate can export nothing but macros. Its
/// full documentation comes with it, below.
///
/// # Examples
///
/// ```
/// // Any path relative to your own crate root. This one is this crate's own
/// // test fixture, because a doctest has to point somewhere real.
/// const SHADER: &str = wgslender::include_wgsl!("tests/fixtures/demo.wgsl");
///
/// assert!(SHADER.contains("@compute"));
/// assert!(SHADER.len() < 500);
/// ```
pub use include_wgsl;
/// Embed a WGSL file compressed, inflating it on first use.
///
/// The same compile-time pipeline as [`include_wgsl!`](include_wgsl), storing
/// DEFLATE-compressed bytes instead of text. Needs the `compress` feature,
/// which is off by default. Its full documentation comes with it, below.
///
/// # Examples
///
/// ```
/// // A `static`, because the constructor is `const` and nothing inflates until
/// // something asks.
/// static SHADER: wgslender::CompressedWgsl =
/// wgslender::include_wgsl_compressed!("tests/fixtures/demo.wgsl");
///
/// assert!(SHADER.compressed_len() < SHADER.len(), "the binary carries the smaller half");
/// assert!(SHADER.as_str().contains("@compute"));
/// ```
pub use include_wgsl_compressed;
/// Generate a Rust module from what a WGSL file declares.
///
/// The shader, the slot of every resource, a `#[repr(C)]` struct for every
/// buffer it takes, and the name of every entry point — worked out at compile
/// time by the same library [`reflect`] calls, and each struct carrying a proof
/// of its own layout. Its full documentation comes with it, below.
///
/// # Examples
///
/// ```
/// // Any path relative to your own crate root. This one is this crate's own
/// // test fixture, because a macro that reads files has to read a real one.
/// wgslender::wgsl_module!(pub demo, "tests/fixtures/demo.wgsl");
///
/// // What a pipeline is built from.
/// assert!(demo::SOURCE.contains("@compute"));
/// assert_eq!(demo::ENTRY_MAIN, "main");
/// assert_eq!(demo::ENTRY_MAIN_WORKGROUP_SIZE, [8, 8, 1]);
/// assert_eq!(demo::bindings::PARAMS, wgslender::BindingSlot { group: 0, binding: 0 });
///
/// // What the uniform buffer holds, laid out as the GPU will read it.
/// let params = demo::Params::new([1920.0, 1080.0], 0.5);
/// assert_eq!(size_of_val(¶ms), 16);
/// assert_eq!(params.resolution, [1920.0, 1080.0]);
/// ```
pub use wgsl_module;