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}