1#![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
33pub const COMPONENTS: &[char] = &['x', 'y', 'z', 'w'];
35pub const INDENT: &str = " ";
37
38pub type NeedBakeExpressions = crate::FastHashSet<crate::Handle<crate::Expression>>;
40
41#[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
64pub type PipelineConstants = hashbrown::HashMap<String, f64>;
72
73#[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#[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
116pub enum FunctionType {
132 Function(crate::Handle<crate::Function>),
134 EntryPoint(crate::proc::EntryPointIndex),
139}
140
141impl FunctionType {
142 pub fn is_compute_like_entry_point(&self, module: &crate::Module) -> bool {
144 match *self {
145 FunctionType::EntryPoint(index) => {
146 module.entry_points[index as usize].stage.compute_like()
147 }
148 FunctionType::Function(_) => false,
149 }
150 }
151}
152
153pub struct FunctionCtx<'a> {
155 pub ty: FunctionType,
157 pub info: &'a crate::valid::FunctionInfo,
159 pub expressions: &'a crate::Arena<crate::Expression>,
161 pub named_expressions: &'a crate::NamedExpressions,
163}
164
165impl FunctionCtx<'_> {
166 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 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 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 pub const fn external_texture_argument_key(
206 &self,
207 arg: u32,
208 external_texture_key: crate::proc::ExternalTextureNameKey,
209 ) -> crate::proc::NameKey {
210 match self.ty {
211 FunctionType::Function(handle) => {
212 crate::proc::NameKey::ExternalTextureFunctionArgument(
213 handle,
214 arg,
215 external_texture_key,
216 )
217 }
218 #[expect(clippy::allow_attributes)]
221 #[allow(clippy::panic)]
222 FunctionType::EntryPoint(_) => {
223 panic!("External textures cannot be used as arguments to entry points")
224 }
225 }
226 }
227
228 pub fn is_fixed_function_input(
230 &self,
231 mut expression: crate::Handle<crate::Expression>,
232 module: &crate::Module,
233 ) -> Option<crate::BuiltIn> {
234 let ep_function = match self.ty {
235 FunctionType::Function(_) => return None,
236 FunctionType::EntryPoint(ep_index) => &module.entry_points[ep_index as usize].function,
237 };
238 let mut built_in = None;
239 loop {
240 match self.expressions[expression] {
241 crate::Expression::FunctionArgument(arg_index) => {
242 return match ep_function.arguments[arg_index as usize].binding {
243 Some(crate::Binding::BuiltIn(bi)) => Some(bi),
244 _ => built_in,
245 };
246 }
247 crate::Expression::AccessIndex { base, index } => {
248 match *self.resolve_type(base, &module.types) {
249 crate::TypeInner::Struct { ref members, .. } => {
250 if let Some(crate::Binding::BuiltIn(bi)) =
251 members[index as usize].binding
252 {
253 built_in = Some(bi);
254 }
255 }
256 _ => return None,
257 }
258 expression = base;
259 }
260 _ => return None,
261 }
262 }
263 }
264}
265
266impl crate::Expression {
267 pub const fn bake_ref_count(&self) -> usize {
276 match *self {
277 crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => usize::MAX,
279 crate::Expression::ImageSample { .. } | crate::Expression::ImageLoad { .. } => 1,
281 crate::Expression::Derivative { .. } => 1,
283 crate::Expression::Load { .. } => 1,
287 _ => 2,
289 }
290 }
291}
292
293pub const fn binary_operation_str(op: crate::BinaryOperator) -> &'static str {
295 use crate::BinaryOperator as Bo;
296 match op {
297 Bo::Add => "+",
298 Bo::Subtract => "-",
299 Bo::Multiply => "*",
300 Bo::Divide => "/",
301 Bo::Modulo => "%",
302 Bo::Equal => "==",
303 Bo::NotEqual => "!=",
304 Bo::Less => "<",
305 Bo::LessEqual => "<=",
306 Bo::Greater => ">",
307 Bo::GreaterEqual => ">=",
308 Bo::And => "&",
309 Bo::ExclusiveOr => "^",
310 Bo::InclusiveOr => "|",
311 Bo::LogicalAnd => "&&",
312 Bo::LogicalOr => "||",
313 Bo::ShiftLeft => "<<",
314 Bo::ShiftRight => ">>",
315 }
316}
317
318impl crate::TypeInner {
319 pub const fn is_handle(&self) -> bool {
321 match *self {
322 crate::TypeInner::Image { .. }
323 | crate::TypeInner::Sampler { .. }
324 | crate::TypeInner::AccelerationStructure { .. } => true,
325 _ => false,
326 }
327 }
328}
329
330impl crate::Statement {
331 pub const fn is_terminator(&self) -> bool {
335 match *self {
336 crate::Statement::Break
337 | crate::Statement::Continue
338 | crate::Statement::Return { .. }
339 | crate::Statement::Kill => true,
340 _ => false,
341 }
342 }
343}
344
345bitflags::bitflags! {
346 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
356 pub struct RayFlag: u32 {
357 const OPAQUE = 0x01;
358 const NO_OPAQUE = 0x02;
359 const TERMINATE_ON_FIRST_HIT = 0x04;
360 const SKIP_CLOSEST_HIT_SHADER = 0x08;
361 const CULL_BACK_FACING = 0x10;
362 const CULL_FRONT_FACING = 0x20;
363 const CULL_OPAQUE = 0x40;
364 const CULL_NO_OPAQUE = 0x80;
365 const SKIP_TRIANGLES = 0x100;
366 const SKIP_AABBS = 0x200;
367 }
368}
369
370#[repr(u32)]
372pub enum RayIntersectionType {
373 Triangle = 1,
374 BoundingBox = 4,
375}