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