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
6#[cfg(feature = "dot-out")]
7pub mod dot;
8#[cfg(feature = "glsl-out")]
9pub mod glsl;
10#[cfg(feature = "hlsl-out")]
11pub mod hlsl;
12#[cfg(feature = "msl-out")]
13pub mod msl;
14#[cfg(feature = "spv-out")]
15pub mod spv;
16#[cfg(feature = "wgsl-out")]
17pub mod wgsl;
18
19const COMPONENTS: &[char] = &['x', 'y', 'z', 'w'];
20const INDENT: &str = "    ";
21const BAKE_PREFIX: &str = "_e";
22
23type NeedBakeExpressions = crate::FastHashSet<crate::Handle<crate::Expression>>;
24
25#[derive(Clone, Copy)]
26struct Level(usize);
27
28impl Level {
29    const fn next(&self) -> Self {
30        Level(self.0 + 1)
31    }
32}
33
34impl std::fmt::Display for Level {
35    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
36        (0..self.0).try_for_each(|_| formatter.write_str(INDENT))
37    }
38}
39
40/// Stores the current function type (either a regular function or an entry point)
41///
42/// Also stores data needed to identify it (handle for a regular function or index for an entry point)
43enum FunctionType {
44    /// A regular function and it's handle
45    Function(crate::Handle<crate::Function>),
46    /// A entry point and it's index
47    EntryPoint(crate::proc::EntryPointIndex),
48}
49
50impl FunctionType {
51    fn is_compute_entry_point(&self, module: &crate::Module) -> bool {
52        match *self {
53            FunctionType::EntryPoint(index) => {
54                module.entry_points[index as usize].stage == crate::ShaderStage::Compute
55            }
56            FunctionType::Function(_) => false,
57        }
58    }
59}
60
61/// Helper structure that stores data needed when writing the function
62struct FunctionCtx<'a> {
63    /// The current function being written
64    ty: FunctionType,
65    /// Analysis about the function
66    info: &'a crate::valid::FunctionInfo,
67    /// The expression arena of the current function being written
68    expressions: &'a crate::Arena<crate::Expression>,
69    /// Map of expressions that have associated variable names
70    named_expressions: &'a crate::NamedExpressions,
71}
72
73impl FunctionCtx<'_> {
74    fn resolve_type<'a>(
75        &'a self,
76        handle: crate::Handle<crate::Expression>,
77        types: &'a crate::UniqueArena<crate::Type>,
78    ) -> &'a crate::TypeInner {
79        self.info[handle].ty.inner_with(types)
80    }
81
82    /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for a local in the current function
83    const fn name_key(&self, local: crate::Handle<crate::LocalVariable>) -> crate::proc::NameKey {
84        match self.ty {
85            FunctionType::Function(handle) => crate::proc::NameKey::FunctionLocal(handle, local),
86            FunctionType::EntryPoint(idx) => crate::proc::NameKey::EntryPointLocal(idx, local),
87        }
88    }
89
90    /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for a function argument.
91    ///
92    /// # Panics
93    /// - If the function arguments are less or equal to `arg`
94    const fn argument_key(&self, arg: u32) -> crate::proc::NameKey {
95        match self.ty {
96            FunctionType::Function(handle) => crate::proc::NameKey::FunctionArgument(handle, arg),
97            FunctionType::EntryPoint(ep_index) => {
98                crate::proc::NameKey::EntryPointArgument(ep_index, arg)
99            }
100        }
101    }
102
103    // Returns true if the given expression points to a fixed-function pipeline input.
104    fn is_fixed_function_input(
105        &self,
106        mut expression: crate::Handle<crate::Expression>,
107        module: &crate::Module,
108    ) -> Option<crate::BuiltIn> {
109        let ep_function = match self.ty {
110            FunctionType::Function(_) => return None,
111            FunctionType::EntryPoint(ep_index) => &module.entry_points[ep_index as usize].function,
112        };
113        let mut built_in = None;
114        loop {
115            match self.expressions[expression] {
116                crate::Expression::FunctionArgument(arg_index) => {
117                    return match ep_function.arguments[arg_index as usize].binding {
118                        Some(crate::Binding::BuiltIn(bi)) => Some(bi),
119                        _ => built_in,
120                    };
121                }
122                crate::Expression::AccessIndex { base, index } => {
123                    match *self.info[base].ty.inner_with(&module.types) {
124                        crate::TypeInner::Struct { ref members, .. } => {
125                            if let Some(crate::Binding::BuiltIn(bi)) =
126                                members[index as usize].binding
127                            {
128                                built_in = Some(bi);
129                            }
130                        }
131                        _ => return None,
132                    }
133                    expression = base;
134                }
135                _ => return None,
136            }
137        }
138    }
139}
140
141impl crate::Expression {
142    /// Returns the ref count, upon reaching which this expression
143    /// should be considered for baking.
144    ///
145    /// Note: we have to cache any expressions that depend on the control flow,
146    /// or otherwise they may be moved into a non-uniform control flow, accidentally.
147    /// See the [module-level documentation][emit] for details.
148    ///
149    /// [emit]: index.html#expression-evaluation-time
150    const fn bake_ref_count(&self) -> usize {
151        match *self {
152            // accesses are never cached, only loads are
153            crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => usize::MAX,
154            // sampling may use the control flow, and image ops look better by themselves
155            crate::Expression::ImageSample { .. } | crate::Expression::ImageLoad { .. } => 1,
156            // derivatives use the control flow
157            crate::Expression::Derivative { .. } => 1,
158            // TODO: We need a better fix for named `Load` expressions
159            // More info - https://github.com/gfx-rs/naga/pull/914
160            // And https://github.com/gfx-rs/naga/issues/910
161            crate::Expression::Load { .. } => 1,
162            // cache expressions that are referenced multiple times
163            _ => 2,
164        }
165    }
166}
167
168/// Helper function that returns the string corresponding to the [`BinaryOperator`](crate::BinaryOperator)
169/// # Notes
170/// Used by `glsl-out`, `msl-out`, `wgsl-out`, `hlsl-out`.
171const fn binary_operation_str(op: crate::BinaryOperator) -> &'static str {
172    use crate::BinaryOperator as Bo;
173    match op {
174        Bo::Add => "+",
175        Bo::Subtract => "-",
176        Bo::Multiply => "*",
177        Bo::Divide => "/",
178        Bo::Modulo => "%",
179        Bo::Equal => "==",
180        Bo::NotEqual => "!=",
181        Bo::Less => "<",
182        Bo::LessEqual => "<=",
183        Bo::Greater => ">",
184        Bo::GreaterEqual => ">=",
185        Bo::And => "&",
186        Bo::ExclusiveOr => "^",
187        Bo::InclusiveOr => "|",
188        Bo::LogicalAnd => "&&",
189        Bo::LogicalOr => "||",
190        Bo::ShiftLeft => "<<",
191        Bo::ShiftRight => ">>",
192    }
193}
194
195/// Helper function that returns the string corresponding to the [`VectorSize`](crate::VectorSize)
196/// # Notes
197/// Used by `msl-out`, `wgsl-out`, `hlsl-out`.
198const fn vector_size_str(size: crate::VectorSize) -> &'static str {
199    match size {
200        crate::VectorSize::Bi => "2",
201        crate::VectorSize::Tri => "3",
202        crate::VectorSize::Quad => "4",
203    }
204}
205
206impl crate::TypeInner {
207    const fn is_handle(&self) -> bool {
208        match *self {
209            crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } => true,
210            _ => false,
211        }
212    }
213}
214
215impl crate::Statement {
216    /// Returns true if the statement directly terminates the current block.
217    ///
218    /// Used to decide whether case blocks require a explicit `break`.
219    pub const fn is_terminator(&self) -> bool {
220        match *self {
221            crate::Statement::Break
222            | crate::Statement::Continue
223            | crate::Statement::Return { .. }
224            | crate::Statement::Kill => true,
225            _ => false,
226        }
227    }
228}
229
230bitflags::bitflags! {
231    /// Ray flags, for a [`RayDesc`]'s `flags` field.
232    ///
233    /// Note that these exactly correspond to the SPIR-V "Ray Flags" mask, and
234    /// the SPIR-V backend passes them directly through to the
235    /// `OpRayQueryInitializeKHR` instruction. (We have to choose something, so
236    /// we might as well make one back end's life easier.)
237    ///
238    /// [`RayDesc`]: crate::Module::generate_ray_desc_type
239    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
240    pub struct RayFlag: u32 {
241        const OPAQUE = 0x01;
242        const NO_OPAQUE = 0x02;
243        const TERMINATE_ON_FIRST_HIT = 0x04;
244        const SKIP_CLOSEST_HIT_SHADER = 0x08;
245        const CULL_BACK_FACING = 0x10;
246        const CULL_FRONT_FACING = 0x20;
247        const CULL_OPAQUE = 0x40;
248        const CULL_NO_OPAQUE = 0x80;
249        const SKIP_TRIANGLES = 0x100;
250        const SKIP_AABBS = 0x200;
251    }
252}
253
254#[repr(u32)]
255enum RayIntersectionType {
256    Triangle = 1,
257    BoundingBox = 4,
258}