naga/back/
mod.rs

1/*!
2Backend functions that export shader [`Module`](super::Module)s into binary and text formats.
3*/
4#![cfg_attr(
5    not(any(dot_out, glsl_out, hlsl_out, msl_out, spv_out, wgsl_out)),
6    allow(
7        dead_code,
8        reason = "shared helpers can be dead if none of the enabled backends need it"
9    )
10)]
11
12use alloc::string::String;
13
14#[cfg(dot_out)]
15pub mod dot;
16#[cfg(glsl_out)]
17pub mod glsl;
18#[cfg(hlsl_out)]
19pub mod hlsl;
20#[cfg(msl_out)]
21pub mod msl;
22#[cfg(spv_out)]
23pub mod spv;
24#[cfg(wgsl_out)]
25pub mod wgsl;
26
27#[cfg(any(hlsl_out, msl_out, spv_out, glsl_out))]
28pub mod pipeline_constants;
29
30#[cfg(any(hlsl_out, glsl_out))]
31mod continue_forward;
32
33/// Names of vector components.
34pub const COMPONENTS: &[char] = &['x', 'y', 'z', 'w'];
35/// Indent for backends.
36pub const INDENT: &str = "    ";
37
38/// Expressions that need baking.
39pub type NeedBakeExpressions = crate::FastHashSet<crate::Handle<crate::Expression>>;
40
41/// A type for displaying expression handles as baking identifiers.
42///
43/// Given an [`Expression`] [`Handle`] `h`, `Baked(h)` implements
44/// [`core::fmt::Display`], showing the handle's index prefixed by
45/// `_e`.
46///
47/// [`Expression`]: crate::Expression
48/// [`Handle`]: crate::Handle
49struct Baked(crate::Handle<crate::Expression>);
50
51impl core::fmt::Display for Baked {
52    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53        self.0.write_prefixed(f, "_e")
54    }
55}
56
57/// Specifies the values of pipeline-overridable constants in the shader module.
58///
59/// If an `@id` attribute was specified on the declaration,
60/// the key must be the pipeline constant ID as a decimal ASCII number; if not,
61/// the key must be the constant's identifier name.
62///
63/// The value may represent any of WGSL's concrete scalar types.
64pub type PipelineConstants = hashbrown::HashMap<String, f64>;
65
66/// Indentation level.
67#[derive(Clone, Copy)]
68pub struct Level(pub usize);
69
70impl Level {
71    pub const fn next(&self) -> Self {
72        Level(self.0 + 1)
73    }
74}
75
76impl core::fmt::Display for Level {
77    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
78        (0..self.0).try_for_each(|_| formatter.write_str(INDENT))
79    }
80}
81
82/// Whether we're generating an entry point or a regular function.
83///
84/// Backend languages often require different code for a [`Function`]
85/// depending on whether it represents an [`EntryPoint`] or not.
86/// Backends can pass common code one of these values to select the
87/// right behavior.
88///
89/// These values also carry enough information to find the `Function`
90/// in the [`Module`]: the `Handle` for a regular function, or the
91/// index into [`Module::entry_points`] for an entry point.
92///
93/// [`Function`]: crate::Function
94/// [`EntryPoint`]: crate::EntryPoint
95/// [`Module`]: crate::Module
96/// [`Module::entry_points`]: crate::Module::entry_points
97pub enum FunctionType {
98    /// A regular function.
99    Function(crate::Handle<crate::Function>),
100    /// An [`EntryPoint`], and its index in [`Module::entry_points`].
101    ///
102    /// [`EntryPoint`]: crate::EntryPoint
103    /// [`Module::entry_points`]: crate::Module::entry_points
104    EntryPoint(crate::proc::EntryPointIndex),
105}
106
107impl FunctionType {
108    /// Returns true if the function is an entry point for a compute shader.
109    pub fn is_compute_entry_point(&self, module: &crate::Module) -> bool {
110        match *self {
111            FunctionType::EntryPoint(index) => {
112                module.entry_points[index as usize].stage == crate::ShaderStage::Compute
113            }
114            FunctionType::Function(_) => false,
115        }
116    }
117}
118
119/// Helper structure that stores data needed when writing the function
120pub struct FunctionCtx<'a> {
121    /// The current function being written
122    pub ty: FunctionType,
123    /// Analysis about the function
124    pub info: &'a crate::valid::FunctionInfo,
125    /// The expression arena of the current function being written
126    pub expressions: &'a crate::Arena<crate::Expression>,
127    /// Map of expressions that have associated variable names
128    pub named_expressions: &'a crate::NamedExpressions,
129}
130
131impl FunctionCtx<'_> {
132    /// Helper method that resolves a type of a given expression.
133    pub fn resolve_type<'a>(
134        &'a self,
135        handle: crate::Handle<crate::Expression>,
136        types: &'a crate::UniqueArena<crate::Type>,
137    ) -> &'a crate::TypeInner {
138        self.info[handle].ty.inner_with(types)
139    }
140
141    /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for a local in the current function
142    pub const fn name_key(
143        &self,
144        local: crate::Handle<crate::LocalVariable>,
145    ) -> crate::proc::NameKey {
146        match self.ty {
147            FunctionType::Function(handle) => crate::proc::NameKey::FunctionLocal(handle, local),
148            FunctionType::EntryPoint(idx) => crate::proc::NameKey::EntryPointLocal(idx, local),
149        }
150    }
151
152    /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for a function argument.
153    ///
154    /// # Panics
155    /// - If the function arguments are less or equal to `arg`
156    pub const fn argument_key(&self, arg: u32) -> crate::proc::NameKey {
157        match self.ty {
158            FunctionType::Function(handle) => crate::proc::NameKey::FunctionArgument(handle, arg),
159            FunctionType::EntryPoint(ep_index) => {
160                crate::proc::NameKey::EntryPointArgument(ep_index, arg)
161            }
162        }
163    }
164
165    /// Returns true if the given expression points to a fixed-function pipeline input.
166    pub fn is_fixed_function_input(
167        &self,
168        mut expression: crate::Handle<crate::Expression>,
169        module: &crate::Module,
170    ) -> Option<crate::BuiltIn> {
171        let ep_function = match self.ty {
172            FunctionType::Function(_) => return None,
173            FunctionType::EntryPoint(ep_index) => &module.entry_points[ep_index as usize].function,
174        };
175        let mut built_in = None;
176        loop {
177            match self.expressions[expression] {
178                crate::Expression::FunctionArgument(arg_index) => {
179                    return match ep_function.arguments[arg_index as usize].binding {
180                        Some(crate::Binding::BuiltIn(bi)) => Some(bi),
181                        _ => built_in,
182                    };
183                }
184                crate::Expression::AccessIndex { base, index } => {
185                    match *self.resolve_type(base, &module.types) {
186                        crate::TypeInner::Struct { ref members, .. } => {
187                            if let Some(crate::Binding::BuiltIn(bi)) =
188                                members[index as usize].binding
189                            {
190                                built_in = Some(bi);
191                            }
192                        }
193                        _ => return None,
194                    }
195                    expression = base;
196                }
197                _ => return None,
198            }
199        }
200    }
201}
202
203impl crate::Expression {
204    /// Returns the ref count, upon reaching which this expression
205    /// should be considered for baking.
206    ///
207    /// Note: we have to cache any expressions that depend on the control flow,
208    /// or otherwise they may be moved into a non-uniform control flow, accidentally.
209    /// See the [module-level documentation][emit] for details.
210    ///
211    /// [emit]: index.html#expression-evaluation-time
212    pub const fn bake_ref_count(&self) -> usize {
213        match *self {
214            // accesses are never cached, only loads are
215            crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => usize::MAX,
216            // sampling may use the control flow, and image ops look better by themselves
217            crate::Expression::ImageSample { .. } | crate::Expression::ImageLoad { .. } => 1,
218            // derivatives use the control flow
219            crate::Expression::Derivative { .. } => 1,
220            // TODO: We need a better fix for named `Load` expressions
221            // More info - https://github.com/gfx-rs/naga/pull/914
222            // And https://github.com/gfx-rs/naga/issues/910
223            crate::Expression::Load { .. } => 1,
224            // cache expressions that are referenced multiple times
225            _ => 2,
226        }
227    }
228}
229
230/// Helper function that returns the string corresponding to the [`BinaryOperator`](crate::BinaryOperator)
231pub const fn binary_operation_str(op: crate::BinaryOperator) -> &'static str {
232    use crate::BinaryOperator as Bo;
233    match op {
234        Bo::Add => "+",
235        Bo::Subtract => "-",
236        Bo::Multiply => "*",
237        Bo::Divide => "/",
238        Bo::Modulo => "%",
239        Bo::Equal => "==",
240        Bo::NotEqual => "!=",
241        Bo::Less => "<",
242        Bo::LessEqual => "<=",
243        Bo::Greater => ">",
244        Bo::GreaterEqual => ">=",
245        Bo::And => "&",
246        Bo::ExclusiveOr => "^",
247        Bo::InclusiveOr => "|",
248        Bo::LogicalAnd => "&&",
249        Bo::LogicalOr => "||",
250        Bo::ShiftLeft => "<<",
251        Bo::ShiftRight => ">>",
252    }
253}
254
255impl crate::TypeInner {
256    /// Returns true if this is a handle to a type rather than the type directly.
257    pub const fn is_handle(&self) -> bool {
258        match *self {
259            crate::TypeInner::Image { .. }
260            | crate::TypeInner::Sampler { .. }
261            | crate::TypeInner::AccelerationStructure { .. } => true,
262            _ => false,
263        }
264    }
265}
266
267impl crate::Statement {
268    /// Returns true if the statement directly terminates the current block.
269    ///
270    /// Used to decide whether case blocks require a explicit `break`.
271    pub const fn is_terminator(&self) -> bool {
272        match *self {
273            crate::Statement::Break
274            | crate::Statement::Continue
275            | crate::Statement::Return { .. }
276            | crate::Statement::Kill => true,
277            _ => false,
278        }
279    }
280}
281
282bitflags::bitflags! {
283    /// Ray flags, for a [`RayDesc`]'s `flags` field.
284    ///
285    /// Note that these exactly correspond to the SPIR-V "Ray Flags" mask, and
286    /// the SPIR-V backend passes them directly through to the
287    /// [`OpRayQueryInitializeKHR`][op] instruction. (We have to choose something, so
288    /// we might as well make one back end's life easier.)
289    ///
290    /// [`RayDesc`]: crate::Module::generate_ray_desc_type
291    /// [op]: https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpRayQueryInitializeKHR
292    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
293    pub struct RayFlag: u32 {
294        const OPAQUE = 0x01;
295        const NO_OPAQUE = 0x02;
296        const TERMINATE_ON_FIRST_HIT = 0x04;
297        const SKIP_CLOSEST_HIT_SHADER = 0x08;
298        const CULL_BACK_FACING = 0x10;
299        const CULL_FRONT_FACING = 0x20;
300        const CULL_OPAQUE = 0x40;
301        const CULL_NO_OPAQUE = 0x80;
302        const SKIP_TRIANGLES = 0x100;
303        const SKIP_AABBS = 0x200;
304    }
305}
306
307/// The intersection test to use for ray queries.
308#[repr(u32)]
309pub enum RayIntersectionType {
310    Triangle = 1,
311    BoundingBox = 4,
312}