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