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
27pub mod pipeline_constants;
28
29#[cfg(any(hlsl_out, glsl_out))]
30mod continue_forward;
31
32pub use nt::TaskDispatchLimits;
33
34pub const COMPONENTS: &[char] = &['x', 'y', 'z', 'w'];
36pub const INDENT: &str = " ";
38
39pub type NeedBakeExpressions = crate::FastHashSet<crate::Handle<crate::Expression>>;
41
42#[cfg_attr(
51 not(any(glsl_out, hlsl_out, msl_out, wgsl_out)),
52 allow(
53 dead_code,
54 reason = "shared helpers can be dead if none of the enabled backends need it"
55 )
56)]
57struct Baked(crate::Handle<crate::Expression>);
58
59impl core::fmt::Display for Baked {
60 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
61 self.0.write_prefixed(f, "_e")
62 }
63}
64
65bitflags::bitflags! {
66 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
68 #[cfg_attr(
69 not(any(hlsl_out, spv_out)),
70 allow(
71 dead_code,
72 reason = "shared helpers can be dead if none of the enabled backends need it"
73 )
74 )]
75 pub(super) struct RayQueryPoint: u32 {
76 const INITIALIZED = 1 << 0;
78 const PROCEED = 1 << 1;
80 const FINISHED_TRAVERSAL = 1 << 2;
82 }
83}
84
85pub type PipelineConstants = hashbrown::HashMap<String, f64>;
93
94#[derive(Clone, Copy, Debug)]
96pub struct Level(pub usize);
97
98impl Level {
99 pub const fn next(&self) -> Self {
100 Level(self.0 + 1)
101 }
102}
103
104impl core::fmt::Display for Level {
105 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
106 (0..self.0).try_for_each(|_| formatter.write_str(INDENT))
107 }
108}
109
110#[cfg(any(hlsl_out, msl_out))]
117fn get_entry_points(
118 module: &crate::ir::Module,
119 entry_point: Option<&(crate::ir::ShaderStage, String)>,
120) -> Result<core::ops::Range<usize>, (crate::ir::ShaderStage, String)> {
121 use alloc::borrow::ToOwned;
122
123 if let Some(&(stage, ref name)) = entry_point {
124 let Some(ep_index) = module
125 .entry_points
126 .iter()
127 .position(|ep| ep.stage == stage && ep.name == *name)
128 else {
129 return Err((stage, name.to_owned()));
130 };
131 Ok(ep_index..ep_index + 1)
132 } else {
133 Ok(0..module.entry_points.len())
134 }
135}
136
137#[derive(Clone, Copy, Debug)]
153pub enum FunctionType {
154 Function(crate::Handle<crate::Function>),
156 EntryPoint(crate::proc::EntryPointIndex),
161}
162
163impl FunctionType {
164 pub fn is_compute_like_entry_point(&self, module: &crate::Module) -> bool {
166 match *self {
167 FunctionType::EntryPoint(index) => {
168 module.entry_points[index as usize].stage.compute_like()
169 }
170 FunctionType::Function(_) => false,
171 }
172 }
173}
174
175#[derive(Debug)]
177pub struct FunctionCtx<'a> {
178 pub ty: FunctionType,
180 pub info: &'a crate::valid::FunctionInfo,
182 pub expressions: &'a crate::Arena<crate::Expression>,
184 pub named_expressions: &'a crate::NamedExpressions,
186}
187
188impl FunctionCtx<'_> {
189 pub fn resolve_type<'a>(
191 &'a self,
192 handle: crate::Handle<crate::Expression>,
193 types: &'a crate::UniqueArena<crate::Type>,
194 ) -> &'a crate::TypeInner {
195 self.info[handle].ty.inner_with(types)
196 }
197
198 pub const fn name_key(
200 &self,
201 local: crate::Handle<crate::LocalVariable>,
202 ) -> crate::proc::NameKey {
203 match self.ty {
204 FunctionType::Function(handle) => crate::proc::NameKey::FunctionLocal(handle, local),
205 FunctionType::EntryPoint(idx) => crate::proc::NameKey::EntryPointLocal(idx, local),
206 }
207 }
208
209 pub const fn argument_key(&self, arg: u32) -> crate::proc::NameKey {
214 match self.ty {
215 FunctionType::Function(handle) => crate::proc::NameKey::FunctionArgument(handle, arg),
216 FunctionType::EntryPoint(ep_index) => {
217 crate::proc::NameKey::EntryPointArgument(ep_index, arg)
218 }
219 }
220 }
221
222 pub const fn external_texture_argument_key(
229 &self,
230 arg: u32,
231 external_texture_key: crate::proc::ExternalTextureNameKey,
232 ) -> crate::proc::NameKey {
233 match self.ty {
234 FunctionType::Function(handle) => {
235 crate::proc::NameKey::ExternalTextureFunctionArgument(
236 handle,
237 arg,
238 external_texture_key,
239 )
240 }
241 #[expect(clippy::allow_attributes)]
244 #[allow(clippy::panic)]
245 FunctionType::EntryPoint(_) => {
246 panic!("External textures cannot be used as arguments to entry points")
247 }
248 }
249 }
250
251 pub fn is_fixed_function_input(
253 &self,
254 mut expression: crate::Handle<crate::Expression>,
255 module: &crate::Module,
256 ) -> Option<crate::BuiltIn> {
257 let ep_function = match self.ty {
258 FunctionType::Function(_) => return None,
259 FunctionType::EntryPoint(ep_index) => &module.entry_points[ep_index as usize].function,
260 };
261 let mut built_in = None;
262 loop {
263 match self.expressions[expression] {
264 crate::Expression::FunctionArgument(arg_index) => {
265 return match ep_function.arguments[arg_index as usize].binding {
266 Some(crate::Binding::BuiltIn(bi)) => Some(bi),
267 _ => built_in,
268 };
269 }
270 crate::Expression::AccessIndex { base, index } => {
271 match *self.resolve_type(base, &module.types) {
272 crate::TypeInner::Struct { ref members, .. } => {
273 if let Some(crate::Binding::BuiltIn(bi)) =
274 members[index as usize].binding
275 {
276 built_in = Some(bi);
277 }
278 }
279 _ => return None,
280 }
281 expression = base;
282 }
283 _ => return None,
284 }
285 }
286 }
287}
288
289impl crate::Expression {
290 pub const fn bake_ref_count(&self) -> usize {
299 match *self {
300 crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => usize::MAX,
302 crate::Expression::ImageSample { .. } | crate::Expression::ImageLoad { .. } => 1,
304 crate::Expression::Derivative { .. } => 1,
306 crate::Expression::Load { .. } => 1,
310 _ => 2,
312 }
313 }
314}
315
316pub const fn binary_operation_str(op: crate::BinaryOperator) -> &'static str {
318 use crate::BinaryOperator as Bo;
319 match op {
320 Bo::Add => "+",
321 Bo::Subtract => "-",
322 Bo::Multiply => "*",
323 Bo::Divide => "/",
324 Bo::Modulo => "%",
325 Bo::Equal => "==",
326 Bo::NotEqual => "!=",
327 Bo::Less => "<",
328 Bo::LessEqual => "<=",
329 Bo::Greater => ">",
330 Bo::GreaterEqual => ">=",
331 Bo::And => "&",
332 Bo::ExclusiveOr => "^",
333 Bo::InclusiveOr => "|",
334 Bo::LogicalAnd => "&&",
335 Bo::LogicalOr => "||",
336 Bo::ShiftLeft => "<<",
337 Bo::ShiftRight => ">>",
338 }
339}
340
341impl crate::TypeInner {
342 pub const fn is_handle(&self) -> bool {
344 match *self {
345 Self::Image { .. } | Self::Sampler { .. } | Self::AccelerationStructure { .. } => true,
346 _ => false,
347 }
348 }
349}
350
351impl crate::Statement {
352 pub const fn is_terminator(&self) -> bool {
356 match *self {
357 crate::Statement::Break
358 | crate::Statement::Continue
359 | crate::Statement::Return { .. }
360 | crate::Statement::Kill => true,
361 _ => false,
362 }
363 }
364}
365
366bitflags::bitflags! {
367 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
377 pub struct RayFlag: u32 {
378 const OPAQUE = 0x01;
379 const NO_OPAQUE = 0x02;
380 const TERMINATE_ON_FIRST_HIT = 0x04;
381 const SKIP_CLOSEST_HIT_SHADER = 0x08;
382 const CULL_BACK_FACING = 0x10;
383 const CULL_FRONT_FACING = 0x20;
384 const CULL_OPAQUE = 0x40;
385 const CULL_NO_OPAQUE = 0x80;
386 const SKIP_TRIANGLES = 0x100;
387 const SKIP_AABBS = 0x200;
388 }
389}
390
391#[derive(Debug)]
393#[repr(u32)]
394pub enum RayIntersectionType {
395 Triangle = 1,
396 BoundingBox = 4,
397}