Skip to main content

naga_rust_back/
config.rs

1use alloc::borrow::Cow;
2use alloc::boxed::Box;
3use alloc::string::String;
4
5use crate::ra;
6use crate::util::GlobalKind;
7
8/// Configuration/builder for options for Rust code generation.
9///
10/// This configuration allows you to control syntactic characteristics of the output,
11/// and also Rust features that have no equivalent in shader languages.
12#[derive(Clone, Debug)]
13pub struct Config {
14    pub(crate) flags: WriterFlags,
15    pub(crate) runtime_path: Cow<'static, str>,
16    pub(crate) global_struct: Option<String>,
17    pub(crate) resource_struct: Option<String>,
18    #[allow(dead_code, reason = "reminding ourselves of the future")]
19    pub(crate) edition: Edition,
20}
21
22impl Default for Config {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl Config {
29    // When adding new options, also add them to:
30    // * `ConfigAndStr::parse` in `macros/src/lib.rs`.
31    // * `embed/src/configuration_syntax.md` (documentation for the macros).
32
33    /// Creates a [`Config`] with default options.
34    #[must_use]
35    pub const fn new() -> Self {
36        Self {
37            flags: WriterFlags::empty(),
38            runtime_path: Cow::Borrowed("::naga_rust_rt"),
39            global_struct: None,
40            resource_struct: None,
41            edition: Edition::Rust2024,
42        }
43    }
44
45    /// Sets whether the generated code contains explicit types when they could be omitted.
46    ///
47    /// The default is `false`.
48    #[must_use]
49    pub fn explicit_types(mut self, value: bool) -> Self {
50        self.flags.set(WriterFlags::EXPLICIT_TYPES, value);
51        self
52    }
53
54    /// Sets whether the generated code uses raw pointers instead of references.
55    ///
56    /// The resulting code may be unsound if the input module uses pointers incorrectly.
57    ///
58    /// <div class="warning">
59    ///
60    /// Currently, this does not actually work, in that it generates code which does not
61    /// even try to `unsafe`ly dereference the raw pointers it uses.
62    /// The exact behavior of this option is not yet decided, but it will likely cause the
63    /// generated functions to be `unsafe fn`s.
64    ///
65    /// </div>
66    ///
67    /// The default is `false`.
68    ///
69    /// TODO: This should be configurable on a per-function basis.
70    #[must_use]
71    pub fn raw_pointers(mut self, value: bool) -> Self {
72        self.flags.set(WriterFlags::RAW_POINTERS, value);
73        self
74    }
75
76    /// Sets whether generated items have `pub` visibility instead of private.
77    ///
78    /// This option applies to all functions or methods, and all fields of generated structs.
79    ///
80    /// The default is `false`.
81    #[must_use]
82    pub fn public_items(mut self, value: bool) -> Self {
83        self.flags.set(WriterFlags::PUBLIC, value);
84        self
85    }
86
87    /// Sets whether to allow the generated code to panic on entering code that cannot be
88    /// translated, rather than failing generation.
89    ///
90    /// This applies to all unsupported expressions and statements, but not to unsupported types.
91    ///
92    /// The default is `false`.
93    #[must_use]
94    pub fn allow_unimplemented(mut self, value: bool) -> Self {
95        self.flags.set(WriterFlags::ALLOW_UNIMPLEMENTED, value);
96        self
97    }
98
99    /// Sets the Rust module path to the runtime support library.
100    ///
101    /// The default is `"::naga_rust_rt"`.
102    ///
103    /// # Panics
104    ///
105    /// May panic if the path is not syntactically valid or not an absolute path.
106    #[must_use]
107    pub fn runtime_path(mut self, value: impl Into<Cow<'static, str>>) -> Self {
108        let value = value.into();
109        assert!(
110            value.starts_with("::") || value.starts_with("crate::"),
111            "path should be an absolute path"
112        );
113        self.runtime_path = value;
114        self
115    }
116
117    /// Allow declarations of global variables, generate a struct with the given `name` to hold
118    /// them, and make all functions methods of that struct.
119    ///
120    /// The struct has one constructor method, which is declared as either
121    /// `const fn new()` or `const fn new(resources: &ResourceStructName)`
122    /// depending on whether [`resource_struct()`][Self::resource_struct] is also set.
123    /// If there are no parameters, then it also implements [`Default`].
124    ///
125    /// If this option is not set, shaders may not contain declarations of variables with
126    /// [address spaces] `private` or `workgroup`.
127    ///
128    /// [address spaces]: https://www.w3.org/TR/WGSL/#address-space
129    #[must_use]
130    pub fn global_struct(mut self, name: impl Into<String>) -> Self {
131        self.global_struct = Some(name.into());
132        self
133    }
134
135    /// Allow declarations of resources (e.g. uniforms), generate a struct with the given `name` to
136    /// hold them, and, if [`global_struct()`][Self::global_struct] is not also set,
137    /// make all functions methods of that struct.
138    ///
139    /// If this option is not set, shaders may not contain declarations of variables with
140    /// [address spaces] `uniform` or `storage`.
141    ///
142    /// [address spaces]: https://www.w3.org/TR/WGSL/#address-space
143    #[must_use]
144    pub fn resource_struct(mut self, name: impl Into<String>) -> Self {
145        self.resource_struct = Some(name.into());
146        self
147    }
148}
149
150/// Internal methods that help generate code based on this config.
151impl Config {
152    /// Returns whether we should generate functions instead of free functions.
153    pub(crate) fn functions_are_methods(&self) -> bool {
154        self.global_struct.is_some() || self.resource_struct.is_some()
155    }
156
157    /// Returns what the self type of our `impl` block is, if we have one.
158    pub(crate) fn impl_type(&self) -> Option<&str> {
159        match self.global_struct {
160            Some(ref name) => Some(name),
161            None => self.resource_struct.as_deref(),
162        }
163    }
164
165    /// Returns the expression for the struct whose fields are the translation of
166    /// shader global variables.
167    pub(crate) fn global_field_access_expr(&self, variable: &naga::GlobalVariable) -> ra::Expr {
168        match (GlobalKind::of_variable(variable), &self.global_struct) {
169            // If we have both resource struct and global struct, the resource struct is
170            // nested inside the global struct.
171            (Some(GlobalKind::Resource), Some(_)) => {
172                ra::Expr::NamedField(Box::new(ra::Expr::Self_), "resources".into())
173            }
174            (Some(GlobalKind::Resource), None) | (Some(GlobalKind::Variable), _) => ra::Expr::Self_,
175            _ => unreachable!(),
176        }
177    }
178}
179
180bitflags::bitflags! {
181    /// Options for what Rust code is generated.
182    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
183    pub(crate) struct WriterFlags: u32 {
184        /// Always annotate the type information instead of inferring.
185        const EXPLICIT_TYPES = 0x1;
186
187        /// Generate code using raw pointers instead of references.
188        /// The resulting code is `unsafe` and may be unsound if the input module
189        /// uses pointers incorrectly.
190        const RAW_POINTERS = 0x2;
191
192        /// Generate items with `pub` visibility instead of private.
193        const PUBLIC = 0x4;
194
195        /// Allow the generated code to panic on entering code that cannot be
196        /// translated, rather than failing generation.
197        const ALLOW_UNIMPLEMENTED = 0x8;
198    }
199}
200
201/// Edition of Rust code to generate.
202///
203/// We currently only support one edition, but this exists anyway to prepare to document
204/// any edition dependencies in the code generator.
205#[derive(Clone, Copy, Debug)]
206pub(crate) enum Edition {
207    Rust2024,
208}