Skip to main content

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}