Skip to main content

naga_rust_back/
config.rs

1use alloc::borrow::Cow;
2use alloc::boxed::Box;
3use alloc::string::String;
4use alloc::vec;
5use alloc::vec::Vec;
6
7use crate::logic::{GlobalLocation, GlobalTranslation};
8use crate::ra;
9
10// -------------------------------------------------------------------------------------------------
11
12/// Configuration/builder for options for Rust code generation.
13///
14/// This configuration allows you to control syntactic characteristics of the output,
15/// and also Rust features that have no equivalent in shader languages.
16#[derive(Clone, Debug)]
17pub struct Config {
18    pub(crate) flags: WriterFlags,
19    pub(crate) runtime_path: Cow<'static, str>,
20    pub(crate) global_struct: Option<String>,
21    pub(crate) resource_struct: Option<String>,
22    #[allow(dead_code, reason = "reminding ourselves of the future")]
23    pub(crate) edition: Edition,
24    pub(crate) rules: Vec<Rule>,
25}
26
27impl Default for Config {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33impl Config {
34    // When adding new options, also add them to:
35    // * `ConfigAndStr::parse` in `macros/src/parse_config.rs`.
36    // * `embed/src/configuration_syntax.md` (documentation for the macros).
37
38    /// Creates a [`Config`] with default options.
39    #[must_use]
40    pub const fn new() -> Self {
41        Self {
42            flags: WriterFlags::INCLUDE_FUNCTIONS,
43            runtime_path: Cow::Borrowed("::naga_rust_rt"),
44            global_struct: None,
45            resource_struct: None,
46            edition: Edition::Rust2024,
47            rules: Vec::new(),
48        }
49    }
50
51    /// Sets whether to translate functions, rather than ignoring them.
52    ///
53    /// This may be disabled to produce an output containing only `struct`s and `const`s,
54    /// and remove any requirement to specify a [`global_struct`][Self::global_struct] or
55    /// [`resource_struct`][Self::resource_struct].
56    ///
57    /// The default is `true`.
58    #[must_use]
59    pub fn include_functions(mut self, value: bool) -> Self {
60        self.flags.set(WriterFlags::INCLUDE_FUNCTIONS, value);
61        self
62    }
63
64    /// Sets whether the generated code contains explicit types when they could be omitted.
65    ///
66    /// The default is `false`.
67    #[must_use]
68    pub fn explicit_types(mut self, value: bool) -> Self {
69        self.flags.set(WriterFlags::EXPLICIT_TYPES, value);
70        self
71    }
72
73    /// Sets whether the generated code uses raw pointers instead of references.
74    ///
75    /// The resulting code may be unsound if the input module uses pointers incorrectly.
76    ///
77    /// <div class="warning">
78    ///
79    /// Currently, this does not actually work, in that it generates code which does not
80    /// even try to `unsafe`ly dereference the raw pointers it uses.
81    /// The exact behavior of this option is not yet decided, but it will likely cause the
82    /// generated functions to be `unsafe fn`s.
83    ///
84    /// </div>
85    ///
86    /// The default is `false`.
87    ///
88    /// TODO: This should be configurable on a per-function basis.
89    #[must_use]
90    pub fn raw_pointers(mut self, value: bool) -> Self {
91        self.flags.set(WriterFlags::RAW_POINTERS, value);
92        self
93    }
94
95    /// Sets whether generated items have `pub` visibility instead of private.
96    ///
97    /// This option applies to all functions, methods, constants, and structs, and
98    /// all fields of generated structs.
99    /// It affects both structs translated from the shader code, and the
100    /// [`global_struct`][Self::global_struct] and [`resource_struct`][Self::resource_struct]
101    /// if present.
102    ///
103    /// The default is `false`.
104    #[must_use]
105    pub fn public_items(mut self, value: bool) -> Self {
106        self.flags.set(WriterFlags::PUBLIC, value);
107        self
108    }
109
110    /// Sets whether to allow the generated code to panic on entering code that cannot be
111    /// translated, rather than failing generation.
112    ///
113    /// This applies to all unsupported expressions and statements, but not to unsupported types.
114    ///
115    /// The default is `false`.
116    #[must_use]
117    pub fn allow_unimplemented(mut self, value: bool) -> Self {
118        self.flags.set(WriterFlags::ALLOW_UNIMPLEMENTED, value);
119        self
120    }
121
122    /// Sets the Rust module path to the runtime support library.
123    ///
124    /// The default is `"::naga_rust_rt"`.
125    ///
126    /// # Panics
127    ///
128    /// May panic if the path is not syntactically valid or not an absolute path.
129    #[must_use]
130    pub fn runtime_path(mut self, value: impl Into<Cow<'static, str>>) -> Self {
131        let value = value.into();
132        assert!(
133            value.starts_with("::") || value.starts_with("crate::"),
134            "path should be an absolute path"
135        );
136        self.runtime_path = value;
137        self
138    }
139
140    /// Allow declarations of global variables, generate a struct with the given `name` to hold
141    /// them, and make all functions methods of that struct.
142    ///
143    /// The struct has one constructor method, which is declared as either
144    /// `const fn new()` or `const fn new(resources: &ResourceStructName)`
145    /// depending on whether [`resource_struct()`][Self::resource_struct] is also set.
146    /// If there are no parameters, then it also implements [`Default`].
147    ///
148    /// If this option is not set, shaders may not contain declarations of variables with
149    /// [address spaces] `private` or `workgroup`.
150    ///
151    /// [address spaces]: https://www.w3.org/TR/WGSL/#address-space
152    #[must_use]
153    pub fn global_struct(mut self, name: impl Into<String>) -> Self {
154        self.global_struct = Some(name.into());
155        self
156    }
157
158    /// Allow declarations of resources (e.g. uniforms), generate a struct with the given `name` to
159    /// hold them, and, if [`global_struct()`][Self::global_struct] is not also set,
160    /// make all functions methods of that struct.
161    ///
162    /// If this option is not set, shaders may not contain declarations of variables with
163    /// [address spaces] `uniform` or `storage`.
164    ///
165    /// [address spaces]: https://www.w3.org/TR/WGSL/#address-space
166    #[must_use]
167    pub fn resource_struct(mut self, name: impl Into<String>) -> Self {
168        self.resource_struct = Some(name.into());
169        self
170    }
171
172    /// Adds a rule.
173    ///
174    /// Rules modify the translation of specific parts of the shader code.
175    /// When multiple rules apply and their effects conflict, rules added later take priority over
176    /// rules added earlier.
177    #[must_use]
178    pub fn rule(mut self, rule: impl Into<Rule>) -> Self {
179        self.rules.push(rule.into());
180        self
181    }
182}
183
184/// Internal methods that help generate code based on this config.
185impl Config {
186    /// Returns whether we should generate functions instead of free functions.
187    pub(crate) fn functions_are_methods(&self) -> bool {
188        self.global_struct.is_some() || self.resource_struct.is_some()
189    }
190
191    /// Returns what the self type of our `impl` block is, if we have one.
192    pub(crate) fn impl_type(&self) -> Option<&str> {
193        match self.global_struct {
194            Some(ref name) => Some(name),
195            None => self.resource_struct.as_deref(),
196        }
197    }
198
199    /// Returns the expression for the struct whose fields are the translation of
200    /// shader global variables.
201    pub(crate) fn global_field_access_expr(
202        &self,
203        module: &naga::Module,
204        variable: &naga::GlobalVariable,
205    ) -> ra::Expr {
206        match (
207            GlobalTranslation::get(module, variable).location,
208            &self.global_struct,
209        ) {
210            // If we have both resource struct and global struct, the resource struct is
211            // nested inside the global struct.
212            (GlobalLocation::Resource, Some(_)) => {
213                ra::Expr::NamedField(Box::new(ra::Expr::Self_), "resources".into())
214            }
215            (GlobalLocation::Resource, None) | (GlobalLocation::Variable, _) => ra::Expr::Self_,
216        }
217    }
218
219    /// Iterates over the effects of all rules that apply in the given situation.
220    ///
221    /// Later rules should take priority over earlier rules (e.g. by overwriting a variable).
222    pub(crate) fn apply_rules(&self, input: &RuleInput<'_>) -> impl Iterator<Item = &Effect> {
223        self.rules.iter().flat_map(
224            |Rule {
225                 conditions: condition,
226                 effects,
227             }| {
228                if condition.iter().all(|c| c.test(input)) {
229                    effects.as_slice()
230                } else {
231                    &[]
232                }
233            },
234        )
235    }
236}
237
238bitflags::bitflags! {
239    /// Options for what Rust code is generated.
240    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
241    pub(crate) struct WriterFlags: u32 {
242        /// Include translated function definitions rather than omitting them.
243        ///
244        /// If this is not set, only `struct` and `const` items are produced.
245        const INCLUDE_FUNCTIONS = 1 << 0;
246
247        /// Always annotate the type information instead of inferring.
248        const EXPLICIT_TYPES = 1 << 1;
249
250        /// Generate code using raw pointers instead of references.
251        /// The resulting code is `unsafe` and may be unsound if the input module
252        /// uses pointers incorrectly.
253        const RAW_POINTERS = 1 << 2;
254
255        /// Generate items with `pub` visibility instead of private.
256        const PUBLIC = 1 << 3;
257
258        /// Allow the generated code to panic on entering code that cannot be
259        /// translated, rather than failing generation.
260        const ALLOW_UNIMPLEMENTED = 1 << 4;
261    }
262}
263
264/// Edition of Rust code to generate.
265///
266/// We currently only support one edition, but this exists anyway to prepare to document
267/// any edition dependencies in the code generator.
268#[derive(Clone, Copy, Debug)]
269pub(crate) enum Edition {
270    Rust2024,
271}
272
273// -------------------------------------------------------------------------------------------------
274
275/// Controls some aspect of the translation from shader code to Rust that needs Rust-specific
276/// choices that cannot be expressed inside the shader code.
277///
278/// Put these in [`Config::rule()`].
279///
280/// When multiple rules apply and their effects conflict, rules added later take priority over
281/// rules added earlier.
282#[derive(Clone, Debug, Eq, PartialEq)]
283#[allow(clippy::exhaustive_structs)]
284pub struct Rule {
285    /// The rule applies when all of these conditions are met.
286    pub conditions: Vec<Condition>,
287    /// The rule has all of these effects.
288    pub effects: Vec<Effect>,
289}
290
291/// Specifies a condition under which a [`Rule`] applies to a part of the input shader code.
292#[derive(Clone, Debug, Eq, PartialEq)]
293#[non_exhaustive]
294pub enum Condition {
295    /// Applies to the shader function with the given name.
296    Function(String),
297    /// Applies to the struct with the given name (excluding resource and global structs).
298    Struct(String),
299    // When adding a variant, update the parser and documentation in `naga-rust-embed` too.
300}
301
302/// Specifies the effect of a [`Rule`].
303#[derive(Clone, Debug, Eq, PartialEq)]
304#[non_exhaustive]
305pub enum Effect {
306    /// Applies a derive macro to translated structs.
307    ///
308    /// The given string is copied literally into the output and must be a path to a derive macro,
309    /// as accepted by the `#[derive]` attribute.
310    Derive(String),
311
312    /// Applies the `#[inline]` attribute to translated functions.
313    ///
314    /// If multiple `Inline` effects apply, then for each function,
315    /// the last choice of inlining wins.
316    ///
317    /// See [The Rust Reference](https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute)
318    /// for information on how the `#[inline]` attribute is interpreted.
319    ///
320    /// Caution: Using this attribute is not always necessary to obtain inlining,
321    /// and inlining is not always beneficial. Usage should be guided by profiling and benchmarks.
322    Inline(Inline),
323    // When adding a variant, update the parser and documentation in `naga-rust-embed` too.
324}
325
326/// What `#[inline]` attribute [`Effect::Inline`] produces.
327///
328/// See [The Rust Reference](https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute)
329/// for information on how the `#[inline]` attribute is interpreted.
330///
331/// Caution: Using this attribute is not always necessary to obtain inlining,
332/// and inlining is not always beneficial. Usage should be guided by profiling and benchmarks.
333#[derive(Clone, Copy, Debug, Eq, PartialEq)]
334#[non_exhaustive]
335pub enum Inline {
336    /// `#[inline]`
337    Maybe,
338    /// `#[inline(always)]`
339    Always,
340    /// `#[inline(never)]`
341    Never,
342}
343
344/// Data consulted by rules.
345pub(crate) struct RuleInput<'a> {
346    /// Shader function name.
347    /// `None` if rules are being applied to something other than a function item,
348    /// or a generated function not corresponding to a shader function.
349    pub function: Option<&'a str>,
350
351    /// Shader struct name.
352    /// `None` if rules are being applied to something other than a struct item,
353    /// or a generated struct not corresponding to a shader struct.
354    pub r#struct: Option<&'a str>,
355}
356
357impl From<(Condition, Effect)> for Rule {
358    fn from((c, e): (Condition, Effect)) -> Self {
359        Self {
360            conditions: vec![c],
361            effects: vec![e],
362        }
363    }
364}
365
366impl From<Effect> for Rule {
367    /// Creates a rule that always applies.
368    fn from(e: Effect) -> Self {
369        Self {
370            conditions: vec![],
371            effects: vec![e],
372        }
373    }
374}
375
376impl Condition {
377    fn test(&self, input: &RuleInput<'_>) -> bool {
378        match *self {
379            Condition::Function(ref name) => input.function == Some(name.as_str()),
380            Condition::Struct(ref name) => input.r#struct == Some(name.as_str()),
381        }
382    }
383}