naga/back/
mod.rs

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