wgsl_rs/std.rs
1//! The WGSL standard library, for Rust.
2//!
3//! When using the module-level `wgsl` proc-macro, this module must only be
4//! glob-imported.
5//!
6//! The glob-imported import statement `use wgsl_rs::std::*` pulls in all the
7//! WGSL types and functions into Rust that are part of the global scope
8//! in WGSL, but don't already exist in Rust's global scope.
9//!
10//! These types include (but are not limited to) vector types like `Vec2f`,
11//! `Vec3f`, etc. and constructors like `vec2`, `vec2f` and `vec3i`, etc.
12
13use std::{
14 any::TypeId,
15 collections::HashMap,
16 ops::{Deref, DerefMut},
17 sync::{Arc, LazyLock, RwLock, RwLockReadGuard, RwLockWriteGuard},
18};
19use wgsl_rs_ir as ir;
20
21// Re-export `PhantomData` so users can write `use wgsl_rs::std::*;` and refer
22// to `PhantomData<T>` as a struct field type. The proc-macro recognizes
23// `PhantomData<_>` fields specially: they are retained in the IR (so
24// extensions can see which type parameter each phantom slot binds) but
25// omitted from the rendered WGSL.
26pub use std::marker::PhantomData;
27
28pub use wgsl_rs_macros::{
29 Wgsl, builtin, compute, fragment, ptr, sampler, storage, texture, uniform, vertex, wgsl_allow,
30 wgsl_ignore, workgroup, workgroup_size,
31};
32
33pub use crate::{discard, get, get_mut, load, slab_copy};
34
35mod atomic;
36mod bitcast;
37mod derivative;
38mod matrix;
39mod numeric;
40mod packing;
41#[cfg(feature = "dispatch-runtime")]
42pub mod runtime;
43mod synchronization;
44mod texture;
45mod vector;
46
47pub use atomic::*;
48pub use bitcast::*;
49pub use derivative::*;
50pub use matrix::*;
51pub use numeric::*;
52pub use packing::*;
53#[cfg(feature = "dispatch-runtime")]
54pub use runtime::*;
55pub use synchronization::*;
56pub use texture::*;
57pub use vector::*;
58
59#[rustfmt::skip] // needed so that rustfmt doesn't merge imports
60#[allow(unused_imports)]
61#[crate::wgsl(crate_path = crate)]
62pub mod builtin_constants {
63 use crate::std::builtin_matrix_constants::*;
64 use crate::std::builtin_vector_constants::*;
65}
66
67pub enum Error {}
68
69/// Marker trait for an owned type that is Send + Sync.
70pub trait AnySendSync: std::any::Any + Send + Sync {}
71impl<T: std::any::Any + Send + Sync> AnySendSync for T {}
72
73/// Trait for Rust types that can be used in WGSL modules.
74pub trait Wgsl: AnySendSync {
75 fn to_ir() -> wgsl_rs_ir::Type;
76}
77
78/// Trait for Rust types that are WGSL scalar types.
79pub trait WgslScalar: Wgsl {
80 fn to_scalar_ir() -> wgsl_rs_ir::ScalarType;
81}
82
83/// Trait for WGSL scalar types that are valid as sampled texture types.
84///
85/// WGSL only allows `f32`, `i32`, and `u32` as the sampled type for
86/// `texture_2d<T>`, `texture_1d<T>`, etc. `bool` is a valid WGSL scalar
87/// but not a valid texture sampled type.
88pub trait WgslTextureScalar: WgslScalar {}
89
90macro_rules! impl_scalar_wgsl {
91 ($t:ty, $i:ident) => {
92 impl WgslScalar for $t {
93 fn to_scalar_ir() -> ir::ScalarType {
94 ir::ScalarType::$i
95 }
96 }
97 impl Wgsl for $t {
98 fn to_ir() -> ir::Type {
99 ir::Type::Scalar(<$t>::to_scalar_ir())
100 }
101 }
102 };
103}
104
105macro_rules! impl_texture_scalar_wgsl {
106 ($t:ty, $i:ident) => {
107 impl_scalar_wgsl!($t, $i);
108 impl WgslTextureScalar for $t {}
109 };
110}
111
112impl_texture_scalar_wgsl!(f32, F32);
113impl_texture_scalar_wgsl!(u32, U32);
114impl_texture_scalar_wgsl!(i32, I32);
115impl_scalar_wgsl!(bool, Bool);
116
117impl<T: Wgsl, const N: usize> Wgsl for [T; N] {
118 fn to_ir() -> ir::Type {
119 ir::Type::Array {
120 elem: Box::new(T::to_ir()),
121 len: ir::Expr::Lit(ir::Lit::Int {
122 digits: N.to_string(),
123 suffix: String::new(),
124 }),
125 }
126 }
127}
128
129/// Shared reference to a uniform, storage or workgroup variable.
130pub struct ModuleVarReadGuard<'a, T> {
131 inner: RwLockReadGuard<'a, TypeMap>,
132 _phantom: PhantomData<T>,
133}
134
135impl<T: AnySendSync> Deref for ModuleVarReadGuard<'_, T> {
136 type Target = T;
137
138 fn deref(&self) -> &Self::Target {
139 let box_any = self
140 .inner
141 .get(&TypeId::of::<T>())
142 .unwrap_or_else(|| panic!("Accessed an uninitialized module variable"));
143 box_any
144 .downcast_ref()
145 .unwrap_or_else(|| panic!("Value was not of type {}", std::any::type_name::<T>()))
146 }
147}
148
149/// Exclusive reference to a storage or workgroup variable.
150pub struct ModuleVarWriteGuard<'a, T> {
151 inner: RwLockWriteGuard<'a, TypeMap>,
152 _phantom: PhantomData<T>,
153}
154
155impl<T: AnySendSync> Deref for ModuleVarWriteGuard<'_, T> {
156 type Target = T;
157
158 fn deref(&self) -> &Self::Target {
159 self.inner
160 .get(&TypeId::of::<T>())
161 .unwrap_or_else(|| panic!("Accessed an uninitialized module variable"))
162 .downcast_ref()
163 .unwrap_or_else(|| panic!("Value was not of type {}", std::any::type_name::<T>()))
164 }
165}
166
167impl<T: AnySendSync> DerefMut for ModuleVarWriteGuard<'_, T> {
168 fn deref_mut(&mut self) -> &mut Self::Target {
169 self.inner
170 .get_mut(&TypeId::of::<T>())
171 .unwrap_or_else(|| panic!("Mutably accessed an uninitialized module variable"))
172 .downcast_mut()
173 .unwrap_or_else(|| panic!("Value was not of type {}", std::any::type_name::<T>()))
174 }
175}
176
177/// A `TypeMap` holds values of any WGSL type, but only one of that type.
178type TypeMap = HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>;
179
180/// Default placeholder type for module-level variables that are generic over
181/// a WGSL type parameter.
182///
183/// When a `uniform!`, `storage!` or `workgroup!` declaration uses an
184/// `impl Trait` type (e.g. `uniform!(group(0), binding(0), FRAME: impl
185/// Convert<f32>)`), the generated Rust `static` is declared as
186/// `Uniform<WgslTypeVariable>` (i.e. `Uniform`). This is because Rust statics
187/// require concrete types and the concrete type is not known at module level.
188///
189/// Such "generic" module variables are accessed via `get_typed::<T>()` /
190/// `set_typed::<T>()` (or the two-arg `get!(VAR, T)` / `get_mut!(VAR, T)`
191/// macros), which use the underlying type-keyed map to read/write a value
192/// of the requested concrete type.
193pub struct WgslTypeVariable;
194
195impl Wgsl for WgslTypeVariable {
196 fn to_ir() -> wgsl_rs_ir::Type {
197 ir::Type::TypeParam {
198 name: "T".to_string(),
199 }
200 }
201}
202
203/// Thread-safe module level variable that can be read from or written
204/// to from anywhere.
205struct ModuleVar<T = WgslTypeVariable> {
206 inner: LazyLock<Arc<RwLock<TypeMap>>>,
207 _phantom: PhantomData<T>,
208}
209
210impl ModuleVar {
211 /// Like `read`, but for dynamic module variables.
212 ///
213 /// ## Panics
214 /// - Panics if the underlying lock has been poisoned.
215 /// - Dereferencing the returned guard will panic if it has not previously
216 /// been set.
217 pub fn get<T: AnySendSync>(&self) -> ModuleVarReadGuard<'_, T> {
218 let lock = self
219 .inner
220 .read()
221 .unwrap_or_else(|_| panic!("could not acquire a read lock on a module variable"));
222 ModuleVarReadGuard {
223 inner: lock,
224 _phantom: PhantomData,
225 }
226 }
227
228 /// Returns a mutable reference to the inner `T`.
229 ///
230 /// ## Panics
231 /// - Panics if the underlying lock has been poisoned.
232 /// - Dereferencing the returned guard will panic if it has not previously
233 /// been set.
234 pub fn get_mut<T: Wgsl>(&self) -> ModuleVarWriteGuard<'_, T> {
235 let lock = self
236 .inner
237 .write()
238 .unwrap_or_else(|_| panic!("could not acquire a write lock on a module variable"));
239 ModuleVarWriteGuard {
240 inner: lock,
241 _phantom: PhantomData,
242 }
243 }
244
245 /// Set the inner data for type `T` on a generic module variable.
246 ///
247 /// ## Panics
248 /// Panics if the underlying lock on the inner data has been poisoned.
249 pub fn set_typed<T: Wgsl>(&self, data: T) {
250 let mut map = self
251 .inner
252 .write()
253 .unwrap_or_else(|_| panic!("could not acquire a write lock on a module variable"));
254 map.insert(TypeId::of::<T>(), Box::new(data));
255 }
256}
257
258impl<T: AnySendSync> ModuleVar<T> {
259 pub const fn new() -> Self {
260 Self {
261 inner: LazyLock::new(Default::default),
262 _phantom: PhantomData,
263 }
264 }
265
266 /// Ensures module variable types adhere to some rules.
267 ///
268 /// ## Panics
269 /// - Panics if the underlying lock has been poisoned.
270 /// - Dereferencing the returned guard will panic if it has not previously
271 /// been set.
272 fn guard_on_generic_use(&self) {
273 if TypeId::of::<T>() == TypeId::of::<WgslTypeVariable>() {
274 panic!(
275 "Requested a generic module variable. All runtime use of generic variables must \
276 be monomorphized."
277 );
278 }
279 }
280
281 /// Returns a reference to the inner `T`.
282 ///
283 /// ## Panics
284 /// - Panics if the variable has not been monomorphized.
285 /// - Panics if the underlying lock has been poisoned.
286 /// - Dereferencing the returned guard will panic if it has not previously
287 /// been set.
288 pub fn read(&self) -> ModuleVarReadGuard<'_, T> {
289 self.guard_on_generic_use();
290
291 let lock = self
292 .inner
293 .read()
294 .unwrap_or_else(|_| panic!("could not acquire a read lock on a module variable"));
295 ModuleVarReadGuard {
296 inner: lock,
297 _phantom: PhantomData,
298 }
299 }
300
301 /// Returns a mutable reference to the inner `T`.
302 ///
303 /// ## Panics
304 /// - Panics if the variable has not been monomorphized.
305 /// - Panics if the underlying lock has been poisoned.
306 /// - Dereferencing the returned guard will panic if it has not previously
307 /// been set.
308 pub fn write(&self) -> ModuleVarWriteGuard<'_, T> {
309 self.guard_on_generic_use();
310
311 let lock = self
312 .inner
313 .write()
314 .unwrap_or_else(|_| panic!("could not acquire a write lock on a module variable"));
315 ModuleVarWriteGuard {
316 inner: lock,
317 _phantom: PhantomData,
318 }
319 }
320
321 /// Set the inner `T`.
322 ///
323 /// ## Panics
324 /// Panics if the underlying lock on the inner data has been poisoned.
325 pub fn set(&self, data: T) {
326 self.guard_on_generic_use();
327
328 let mut map = self
329 .inner
330 .write()
331 .unwrap_or_else(|_| panic!("could not acquire a write lock on a module variable"));
332 map.insert(TypeId::of::<T>(), Box::new(data));
333 }
334}
335
336/// A workgroup variable.
337pub struct Workgroup<T = WgslTypeVariable> {
338 data: ModuleVar<T>,
339}
340
341impl<T: Wgsl> Workgroup<T> {
342 /// Creates a new workgroup variable.
343 #[allow(clippy::new_without_default)]
344 pub const fn new() -> Self {
345 Self {
346 data: ModuleVar::new(),
347 }
348 }
349
350 /// Get a reference to the inner `T`.
351 ///
352 /// Not available in WGSL.
353 ///
354 /// ## Panics
355 /// Dereferencing the returned guard will panic if it has not previously
356 /// been set.
357 pub fn get(&self) -> ModuleVarReadGuard<'_, T> {
358 self.data.read()
359 }
360
361 /// Get a mutable reference to the inner `T`.
362 ///
363 /// Not available in WGSL.
364 ///
365 /// ## Panics
366 /// Dereferencing the returned guard will panic if it has not previously
367 /// been set.
368 pub fn get_mut(&self) -> ModuleVarWriteGuard<'_, T> {
369 self.data.write()
370 }
371
372 /// Set the inner `T`.
373 ///
374 /// Not available in WGSL.
375 pub fn set(&self, data: T) {
376 self.data.set(data);
377 }
378}
379
380impl Workgroup<WgslTypeVariable> {
381 /// Get a reference to the inner data, as a concrete type `T`.
382 ///
383 /// This is the dynamic accessor for a "generic" workgroup variable
384 /// (one whose type was a type parameter of an entry point).
385 ///
386 /// ## Panics
387 /// - Panics if the underlying lock has been poisoned.
388 /// - Dereferencing the returned guard panics if no value has been set for
389 /// `T`.
390 pub fn get_typed<T: Wgsl>(&self) -> ModuleVarReadGuard<'_, T> {
391 self.data.get::<T>()
392 }
393
394 /// Get a mutable reference to the inner data, as a concrete type `T`.
395 ///
396 /// ## Panics
397 /// - Panics if the underlying lock has been poisoned.
398 /// - Dereferencing the returned guard panics if no value has been set for
399 /// `T`.
400 pub fn get_mut_typed<T: Wgsl>(&self) -> ModuleVarWriteGuard<'_, T> {
401 self.data.get_mut::<T>()
402 }
403
404 /// Set the inner data for type `T` on a generic workgroup variable.
405 pub fn set_typed<T: Wgsl>(&self, data: T) {
406 self.data.set_typed::<T>(data);
407 }
408}
409
410/// A shader uniform, backed by a `RwLock<T>` on the CPU.
411pub struct Uniform<T = WgslTypeVariable> {
412 pub group: u32,
413 pub binding: u32,
414 data: ModuleVar<T>,
415}
416
417impl<T: Wgsl> Uniform<T> {
418 /// Creates a new uniform.
419 pub const fn new(group: u32, binding: u32) -> Self {
420 Self {
421 group,
422 binding,
423 data: ModuleVar::new(),
424 }
425 }
426
427 /// Returns a reference to the inner `T`.
428 ///
429 /// ## Panics
430 /// Dereferencing the returned guard will panic if it has not previously
431 /// been set.
432 pub fn get(&self) -> ModuleVarReadGuard<'_, T> {
433 self.data.read()
434 }
435
436 /// Set the inner `T`.
437 ///
438 /// Not available in WGSL.
439 ///
440 /// ## Note
441 /// Though this method is public, using it in a `#[wgsl]` module triggers
442 /// a parse error.
443 ///
444 /// This is fine because uniform values are read-only from a shader.
445 ///
446 /// This method still exists to set the value for Rust CPU shader testing.
447 pub fn set(&self, data: T) {
448 self.data.set(data);
449 }
450}
451
452impl Uniform<WgslTypeVariable> {
453 /// Get a reference to the inner data, as a concrete type `T`.
454 ///
455 /// This is the dynamic accessor for a "generic" uniform (one whose type
456 /// was a type parameter of an entry point). Use the two-arg form of the
457 /// `get!` macro: `get!(FRAME, T)`.
458 ///
459 /// ## Panics
460 /// - Panics if the underlying lock has been poisoned.
461 /// - Dereferencing the returned guard panics if no value has been set for
462 /// `T`.
463 pub fn get_typed<T: Wgsl>(&self) -> ModuleVarReadGuard<'_, T> {
464 self.data.get::<T>()
465 }
466
467 /// Set the inner data for type `T` on a generic uniform variable.
468 ///
469 /// Not available in WGSL.
470 pub fn set_typed<T: Wgsl>(&self, data: T) {
471 self.data.set_typed::<T>(data);
472 }
473}
474
475/// Marker type for read-only access-mode.
476pub struct Read;
477
478/// Marker type for write-only access-mode.
479pub struct Write;
480
481/// Marker type for readwrite access-mode.
482pub struct ReadWrite;
483
484/// A shader storage buffer, backed by a `T`.
485pub struct Storage<T = WgslTypeVariable, AM = Read> {
486 group: u32,
487 binding: u32,
488 access_mode: PhantomData<AM>,
489 data: ModuleVar<T>,
490}
491
492/// Marker trait for read or readwrite storage.
493pub trait AccessMode {}
494
495impl AccessMode for Read {}
496impl AccessMode for ReadWrite {}
497
498impl<T: Wgsl, AM: AccessMode> Storage<T, AM> {
499 pub const fn new(group: u32, binding: u32) -> Self {
500 Storage {
501 data: ModuleVar::new(),
502 group,
503 binding,
504 access_mode: PhantomData,
505 }
506 }
507
508 /// Returns a reference to the inner `T`.
509 ///
510 /// Not available in WGSL. Use [`get!`] instead.
511 ///
512 /// ## Panics
513 /// Dereferencing the returned guard will panic if it has not previously
514 /// been set.
515 pub fn get(&self) -> ModuleVarReadGuard<'_, T> {
516 self.data.read()
517 }
518
519 /// Returns a mutable reference to the inner `T`.
520 ///
521 /// Not available in WGSL. Use [`get_mut!`] instead.
522 ///
523 /// ## Panics
524 /// Dereferencing the returned guard will panic if it has not previously
525 /// been set.
526 pub fn get_mut(&self) -> ModuleVarWriteGuard<'_, T> {
527 self.data.write()
528 }
529
530 /// Set the inner `T`.
531 ///
532 /// Not available in WGSL. Used for CPU-side shader testing to
533 /// populate storage buffers before calling compute entry points.
534 pub fn set(&self, data: T) {
535 self.data.set(data);
536 }
537
538 /// Returns the group index of this storage variable.
539 pub fn group(&self) -> u32 {
540 self.group
541 }
542
543 /// Returns the binding index within its group of this storage variable.
544 pub fn binding(&self) -> u32 {
545 self.binding
546 }
547}
548
549impl<AM: AccessMode> Storage<WgslTypeVariable, AM> {
550 /// Get a reference to the inner data, as a concrete type `T`.
551 ///
552 /// This is the dynamic accessor for a "generic" storage variable (one
553 /// whose type was a type parameter of an entry point). Use the two-arg
554 /// form of the `get!` macro: `get!(BUFFER, T)`.
555 ///
556 /// ## Panics
557 /// - Panics if the underlying lock has been poisoned.
558 /// - Dereferencing the returned guard panics if no value has been set for
559 /// `T`.
560 pub fn get_typed<T: Wgsl>(&self) -> ModuleVarReadGuard<'_, T> {
561 self.data.get::<T>()
562 }
563
564 /// Get a mutable reference to the inner data, as a concrete type `T`.
565 ///
566 /// This is the dynamic accessor for a "generic" storage variable. Use
567 /// the two-arg form of the `get_mut!` macro: `get_mut!(BUFFER, T)`.
568 ///
569 /// ## Panics
570 /// - Panics if the underlying lock has been poisoned.
571 /// - Dereferencing the returned guard panics if no value has been set for
572 /// `T`.
573 pub fn get_mut_typed<T: Wgsl>(&self) -> ModuleVarWriteGuard<'_, T> {
574 self.data.get_mut::<T>()
575 }
576
577 /// Set the inner data for type `T` on a generic storage variable.
578 pub fn set_typed<T: Wgsl>(&self, data: T) {
579 self.data.set_typed::<T>(data);
580 }
581}
582
583/// Provides access to a storage, uniform, or workgroup variable.
584///
585/// Since module variables are `static` and implemented with locks,
586/// normal borrows aren't possible.
587///
588/// # Forms
589///
590/// - `get!(VAR)` — reads the inner value of a concrete module variable.
591/// - `get!(VAR, T)` — reads the inner value of a *generic* module variable,
592/// downcasting to the concrete type `T`. The variable must have been declared
593/// with an `impl Trait` type (e.g. `uniform!(group(0), binding(0), FRAME:
594/// impl Convert<f32>)`); using this form on a non-generic variable is a
595/// compile-time error.
596///
597/// # Example
598/// ```ignore
599/// storage!(group(0), binding(0), read_write, OUTPUT: [f32; 256]);
600///
601/// let value = get!(OUTPUT)[idx];
602/// let value = get!(OUTPUT).field;
603///
604/// // Generic uniform usage:
605/// uniform!(group(0), binding(0), FRAME: impl Convert<f32>);
606///
607/// fn use_frame<T: Wgsl + Convert<f32>>() -> f32 {
608/// f32(get!(FRAME, T))
609/// }
610/// ```
611#[macro_export]
612macro_rules! get {
613 ($var:ident) => {
614 $var.get()
615 };
616 ($var:ident, $ty:ty) => {
617 $var.get_typed::<$ty>()
618 };
619}
620
621/// Provides mutable access to a storage or workgroup variable.
622///
623/// Since module variables are `static` and implemented with locks,
624/// normal mutable borrows aren't possible.
625/// This macro uses interior mutability to enable writes.
626///
627/// # Forms
628///
629/// - `get_mut!(VAR)` — mutably accesses a concrete module variable.
630/// - `get_mut!(VAR, T)` — mutably accesses a *generic* module variable,
631/// downcasting to the concrete type `T`. Using this form on a non-generic
632/// variable is a compile-time error.
633///
634/// # Example
635/// ```ignore
636/// storage!(group(0), binding(0), read_write, OUTPUT: [f32; 256]);
637///
638/// get_mut!(OUTPUT)[idx] = value;
639/// get_mut!(OUTPUT).field = value;
640/// ```
641#[macro_export]
642macro_rules! get_mut {
643 ($var:ident) => {
644 $var.get_mut()
645 };
646 ($var:ident, $ty:ty) => {
647 $var.get_mut_typed::<$ty>()
648 };
649}
650
651/// Copies the value of a uniform, storage, or workgroup variable into a local.
652///
653/// `get!` returns a lock guard that dereferences to `&T`, which cannot be used
654/// directly in arithmetic. `load!` copies the value out of the guard and
655/// returns plain `T` by value, so it works in expressions. In WGSL this
656/// transpiles to the bare variable name (a plain value read).
657///
658/// The value type must be `Copy`; all built-in WGSL value types are.
659///
660/// # Forms
661///
662/// - `load!(VAR)` — loads the value of a concrete module variable.
663/// - `load!(VAR, T)` — loads the value of a *generic* module variable,
664/// downcasting to the concrete type `T` (like `get!(VAR, T)`).
665///
666/// # Example
667/// ```ignore
668/// uniform!(group(0), binding(0), U_RESOLUTION: Vec2f);
669///
670/// // Value semantics: usable directly in arithmetic
671/// let st = frag_coord.xy() / load!(U_RESOLUTION) * 3.0;
672/// ```
673#[macro_export]
674macro_rules! load {
675 ($var:ident) => {{
676 // The `let` binding forces the copy into an owned value; `*$var.get()`
677 // alone would be a place expression that still borrows the guard.
678 let v = *$var.get();
679 v
680 }};
681 ($var:ident, $ty:ty) => {{
682 let v = *$var.get_typed::<$ty>();
683 v
684 }};
685}
686
687/// Copy `$size` elements from `$src` starting at `$src_offset` into `$dest`
688/// starting at `$dest_offset`.
689///
690/// On the GPU side (inside `#[wgsl]` modules), this emits a WGSL `for` loop
691/// of the form `dest[dest_offset + i] = src[src_offset + i]`.
692///
693/// On the CPU side, this is a simple element-by-element copy.
694///
695/// The macro is bidirectional: pass the slab as `$src` to read from a storage
696/// buffer into a local array, or pass the slab as `$dest` to write from a
697/// local array into a storage buffer.
698///
699/// # Example
700/// ```ignore
701/// let mut raw = [0u32; 4];
702/// slab_copy!(get!(SLAB), offset, raw, 0, 4);
703/// ```
704#[macro_export]
705macro_rules! slab_copy {
706 ($src:expr, $src_offset:expr, $dest:expr, $dest_offset:expr, $size:expr) => {{
707 let src_offset = $src_offset as usize;
708 let dest_offset = $dest_offset as usize;
709 for i in 0..$size as usize {
710 $dest[dest_offset + i] = $src[src_offset + i];
711 }
712 }};
713}
714
715/// Marks the current fragment invocation as discarded.
716///
717/// In WGSL, this transpiles to `discard;`, which converts the invocation
718/// into a helper invocation and prevents the fragment from being processed
719/// downstream in the render pipeline.
720///
721/// On the CPU side (when running via `dispatch_fragments`), this sets a
722/// thread-local flag. The shader continues executing (matching WGSL
723/// semantics where helper invocations continue for derivative computation),
724/// but `dispatch_fragments` suppresses the output.
725///
726/// # Example
727/// ```ignore
728/// if depth < 0.001 {
729/// discard!();
730/// }
731/// ```
732#[macro_export]
733macro_rules! discard {
734 () => {
735 $crate::std::mark_discarded()
736 };
737}
738
739/// CPU-side implementation of `discard!()`.
740///
741/// When the `dispatch-runtime` feature is enabled, this delegates to
742/// the runtime's thread-local discard flag. Otherwise, it is a no-op.
743#[inline]
744pub fn mark_discarded() {
745 #[cfg(feature = "dispatch-runtime")]
746 {
747 runtime::mark_discarded();
748 }
749}
750
751/// Used to provide WGSL type conversion functions like `f32(...)`, etc.
752pub trait Convert<T> {
753 fn convert(self) -> T;
754}
755
756impl<A: Clone + Convert<B>, B> Convert<B> for &A {
757 fn convert(self) -> B {
758 let a = self.clone();
759 a.convert()
760 }
761}
762
763impl Convert<f32> for f32 {
764 fn convert(self) -> f32 {
765 self
766 }
767}
768
769impl Convert<u32> for u32 {
770 fn convert(self) -> u32 {
771 self
772 }
773}
774
775impl Convert<i32> for i32 {
776 fn convert(self) -> i32 {
777 self
778 }
779}
780
781impl<A: Clone + Convert<B> + Wgsl, B> Convert<B> for ModuleVarReadGuard<'_, A> {
782 fn convert(self) -> B {
783 (*self).clone().convert()
784 }
785}
786
787impl<A: Clone + Convert<B> + Wgsl, B> Convert<B> for ModuleVarWriteGuard<'_, A> {
788 fn convert(self) -> B {
789 (*self).clone().convert()
790 }
791}
792
793macro_rules! impl_convert_as {
794 ($from:ty, $to:ty) => {
795 impl Convert<$to> for $from {
796 fn convert(self) -> $to {
797 self as $to
798 }
799 }
800 };
801}
802impl_convert_as!(f32, u32);
803impl_convert_as!(f32, i32);
804impl_convert_as!(i32, f32);
805impl_convert_as!(i32, u32);
806impl_convert_as!(u32, f32);
807impl_convert_as!(u32, i32);
808
809/// Returns the input cast to f32.
810pub fn f32(t: impl Convert<f32>) -> f32 {
811 t.convert()
812}
813
814/// Returns the input cast to u32.
815pub fn u32(t: impl Convert<u32>) -> u32 {
816 t.convert()
817}
818
819/// Returns the input cast to i32.
820pub fn i32(t: impl Convert<i32>) -> i32 {
821 t.convert()
822}
823
824/// A runtime-sized array, backed by a [`Vec`] on CPU.
825///
826/// In WGSL, this transpiles to `array<T>` (no size parameter).
827/// Runtime-sized arrays can only be used in storage buffers,
828/// typically raw, or as the last field of a struct.
829#[derive(Debug, Clone, Default)]
830pub struct RuntimeArray<T> {
831 pub data: std::vec::Vec<T>,
832}
833
834impl<T: Wgsl> Wgsl for RuntimeArray<T> {
835 fn to_ir() -> ir::Type {
836 ir::Type::RuntimeArray {
837 elem: Box::new(T::to_ir()),
838 }
839 }
840}
841
842impl<T> RuntimeArray<T> {
843 /// Create a new `RuntimeArray<T>` on the CPU.
844 ///
845 /// Only available on CPU, this is a parse error in `#[wgsl]` modules.
846 pub fn new() -> Self {
847 Self {
848 data: std::vec::Vec::new(),
849 }
850 }
851
852 /// Creates a new `RuntimeArray<T>` with a starting `capacity`.
853 ///
854 /// Only available on CPU, this is a parse error in `#[wgsl]` modules.
855 pub fn with_capacity(capacity: usize) -> Self {
856 Self {
857 data: std::vec::Vec::with_capacity(capacity),
858 }
859 }
860
861 /// Returns the length of this `RuntimeArray<T>`.
862 ///
863 /// Only available on CPU, this is a parse error in `#[wgsl]` modules.
864 /// Use the builtin function [`array_length`] in `#[wgsl]` modules.
865 pub fn len(&self) -> u32 {
866 self.data.len() as u32
867 }
868
869 /// Returns whether this `RuntimeArray<T>` is empty.
870 ///
871 /// Only available on CPU, this is a parse error in `#[wgsl]` modules.
872 pub fn is_empty(&self) -> bool {
873 self.data.is_empty()
874 }
875
876 /// Append `value` onto the back of the collection.
877 ///
878 /// Only available on CPU, this is a parse error in `#[wgsl]` modules.
879 pub fn push(&mut self, value: T) {
880 self.data.push(value);
881 }
882
883 /// Clears the array, removing all values.
884 ///
885 /// Only available on CPU, this is a parse error in `#[wgsl]` modules.
886 pub fn clear(&mut self) {
887 self.data.clear();
888 }
889}
890
891// Index by usize (Rust standard)
892impl<T> std::ops::Index<usize> for RuntimeArray<T> {
893 type Output = T;
894 fn index(&self, index: usize) -> &Self::Output {
895 &self.data[index]
896 }
897}
898
899impl<T> std::ops::IndexMut<usize> for RuntimeArray<T> {
900 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
901 &mut self.data[index]
902 }
903}
904
905// Index by u32 (for WGSL compatibility)
906impl<T> std::ops::Index<u32> for RuntimeArray<T> {
907 type Output = T;
908 fn index(&self, index: u32) -> &Self::Output {
909 &self.data[index as usize]
910 }
911}
912
913impl<T> std::ops::IndexMut<u32> for RuntimeArray<T> {
914 fn index_mut(&mut self, index: u32) -> &mut Self::Output {
915 &mut self.data[index as usize]
916 }
917}
918
919/// Trait for types that support the WGSL `arrayLength` builtin function.
920///
921/// In WGSL, `arrayLength` returns the number of elements in a runtime-sized
922/// array. This trait provides the Rust-side equivalent for `RuntimeArray<T>`.
923///
924/// # WGSL Equivalent
925///
926/// ```wgsl
927/// @group(0) @binding(0) var<storage> data: array<f32>;
928///
929/// fn example() -> u32 {
930/// return arrayLength(&data);
931/// }
932/// ```
933///
934/// # Note
935///
936/// In WGSL, `arrayLength` takes a pointer to a runtime-sized array. On the Rust
937/// side, we use a reference instead. The `array_length` free function provides
938/// the same interface as the WGSL builtin.
939pub trait ArrayLength {
940 fn array_length(self) -> u32;
941}
942
943impl<T> ArrayLength for &RuntimeArray<T> {
944 fn array_length(self) -> u32 {
945 self.data.len() as u32
946 }
947}
948
949/// Returns the number of elements in a runtime-sized array.
950///
951/// This is the Rust-side equivalent of the WGSL `arrayLength` builtin function.
952///
953/// # Example
954///
955/// ```ignore
956/// use wgsl_rs::std::*;
957///
958/// let mut particles: RuntimeArray<f32> = RuntimeArray::new();
959/// particles.push(1.0);
960/// particles.push(2.0);
961/// particles.push(3.0);
962///
963/// assert_eq!(array_length(&particles), 3);
964/// ```
965///
966/// # WGSL Equivalent
967///
968/// In WGSL, you would write:
969///
970/// ```wgsl
971/// let len: u32 = arrayLength(&runtime_array);
972/// ```
973pub fn array_length(array: impl ArrayLength) -> u32 {
974 array.array_length()
975}
976
977#[cfg(test)]
978mod tests {
979 use super::*;
980
981 #[test]
982 fn load_copies_concrete_module_var_values() {
983 static U: Uniform<f32> = Uniform::new(0, 0);
984 static S: Storage<f32, Read> = Storage::new(0, 1);
985 static W: Workgroup<f32> = Workgroup::new();
986
987 U.set(1.5_f32);
988 S.set(2.5_f32);
989 W.set(3.5_f32);
990
991 assert_eq!(load!(U), 1.5_f32);
992 assert_eq!(load!(S), 2.5_f32);
993 assert_eq!(load!(W), 3.5_f32);
994 }
995
996 #[test]
997 fn load_typed_copies_generic_module_var_values() {
998 static G: Uniform = Uniform::new(0, 0);
999 G.set_typed(4.5_f32);
1000 assert_eq!(load!(G, f32), 4.5_f32);
1001 }
1002}