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
64bitflags::bitflags! {
65 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
67 #[cfg_attr(
68 not(any(hlsl_out, spv_out)),
69 allow(
70 dead_code,
71 reason = "shared helpers can be dead if none of the enabled backends need it"
72 )
73 )]
74 pub(super) struct RayQueryPoint: u32 {
75 const INITIALIZED = 1 << 0;
77 const PROCEED = 1 << 1;
79 const FINISHED_TRAVERSAL = 1 << 2;
81 }
82}
83
84pub type PipelineConstants = hashbrown::HashMap<String, f64>;
92
93#[derive(Clone, Copy)]
95pub struct Level(pub usize);
96
97impl Level {
98 pub const fn next(&self) -> Self {
99 Level(self.0 + 1)
100 }
101}
102
103impl core::fmt::Display for Level {
104 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
105 (0..self.0).try_for_each(|_| formatter.write_str(INDENT))
106 }
107}
108
109#[cfg(any(hlsl_out, msl_out))]
116fn get_entry_points(
117 module: &crate::ir::Module,
118 entry_point: Option<&(crate::ir::ShaderStage, String)>,
119) -> Result<core::ops::Range<usize>, (crate::ir::ShaderStage, String)> {
120 use alloc::borrow::ToOwned;
121
122 if let Some(&(stage, ref name)) = entry_point {
123 let Some(ep_index) = module
124 .entry_points
125 .iter()
126 .position(|ep| ep.stage == stage && ep.name == *name)
127 else {
128 return Err((stage, name.to_owned()));
129 };
130 Ok(ep_index..ep_index + 1)
131 } else {
132 Ok(0..module.entry_points.len())
133 }
134}
135
136#[derive(Clone, Copy, Debug)]
152pub enum FunctionType {
153 Function(crate::Handle<crate::Function>),
155 EntryPoint(crate::proc::EntryPointIndex),
160}
161
162impl FunctionType {
163 pub fn is_compute_like_entry_point(&self, module: &crate::Module) -> bool {
165 match *self {
166 FunctionType::EntryPoint(index) => {
167 module.entry_points[index as usize].stage.compute_like()
168 }
169 FunctionType::Function(_) => false,
170 }
171 }
172}
173
174pub struct FunctionCtx<'a> {
176 pub ty: FunctionType,
178 pub info: &'a crate::valid::FunctionInfo,
180 pub expressions: &'a crate::Arena<crate::Expression>,
182 pub named_expressions: &'a crate::NamedExpressions,
184}
185
186impl FunctionCtx<'_> {
187 pub fn resolve_type<'a>(
189 &'a self,
190 handle: crate::Handle<crate::Expression>,
191 types: &'a crate::UniqueArena<crate::Type>,
192 ) -> &'a crate::TypeInner {
193 self.info[handle].ty.inner_with(types)
194 }
195
196 pub const fn name_key(
198 &self,
199 local: crate::Handle<crate::LocalVariable>,
200 ) -> crate::proc::NameKey {
201 match self.ty {
202 FunctionType::Function(handle) => crate::proc::NameKey::FunctionLocal(handle, local),
203 FunctionType::EntryPoint(idx) => crate::proc::NameKey::EntryPointLocal(idx, local),
204 }
205 }
206
207 pub const fn argument_key(&self, arg: u32) -> crate::proc::NameKey {
212 match self.ty {
213 FunctionType::Function(handle) => crate::proc::NameKey::FunctionArgument(handle, arg),
214 FunctionType::EntryPoint(ep_index) => {
215 crate::proc::NameKey::EntryPointArgument(ep_index, arg)
216 }
217 }
218 }
219
220 pub const fn external_texture_argument_key(
227 &self,
228 arg: u32,
229 external_texture_key: crate::proc::ExternalTextureNameKey,
230 ) -> crate::proc::NameKey {
231 match self.ty {
232 FunctionType::Function(handle) => {
233 crate::proc::NameKey::ExternalTextureFunctionArgument(
234 handle,
235 arg,
236 external_texture_key,
237 )
238 }
239 #[expect(clippy::allow_attributes)]
242 #[allow(clippy::panic)]
243 FunctionType::EntryPoint(_) => {
244 panic!("External textures cannot be used as arguments to entry points")
245 }
246 }
247 }
248
249 pub fn is_fixed_function_input(
251 &self,
252 mut expression: crate::Handle<crate::Expression>,
253 module: &crate::Module,
254 ) -> Option<crate::BuiltIn> {
255 let ep_function = match self.ty {
256 FunctionType::Function(_) => return None,
257 FunctionType::EntryPoint(ep_index) => &module.entry_points[ep_index as usize].function,
258 };
259 let mut built_in = None;
260 loop {
261 match self.expressions[expression] {
262 crate::Expression::FunctionArgument(arg_index) => {
263 return match ep_function.arguments[arg_index as usize].binding {
264 Some(crate::Binding::BuiltIn(bi)) => Some(bi),
265 _ => built_in,
266 };
267 }
268 crate::Expression::AccessIndex { base, index } => {
269 match *self.resolve_type(base, &module.types) {
270 crate::TypeInner::Struct { ref members, .. } => {
271 if let Some(crate::Binding::BuiltIn(bi)) =
272 members[index as usize].binding
273 {
274 built_in = Some(bi);
275 }
276 }
277 _ => return None,
278 }
279 expression = base;
280 }
281 _ => return None,
282 }
283 }
284 }
285}
286
287impl crate::Expression {
288 pub const fn bake_ref_count(&self) -> usize {
297 match *self {
298 crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => usize::MAX,
300 crate::Expression::ImageSample { .. } | crate::Expression::ImageLoad { .. } => 1,
302 crate::Expression::Derivative { .. } => 1,
304 crate::Expression::Load { .. } => 1,
308 _ => 2,
310 }
311 }
312}
313
314pub const fn binary_operation_str(op: crate::BinaryOperator) -> &'static str {
316 use crate::BinaryOperator as Bo;
317 match op {
318 Bo::Add => "+",
319 Bo::Subtract => "-",
320 Bo::Multiply => "*",
321 Bo::Divide => "/",
322 Bo::Modulo => "%",
323 Bo::Equal => "==",
324 Bo::NotEqual => "!=",
325 Bo::Less => "<",
326 Bo::LessEqual => "<=",
327 Bo::Greater => ">",
328 Bo::GreaterEqual => ">=",
329 Bo::And => "&",
330 Bo::ExclusiveOr => "^",
331 Bo::InclusiveOr => "|",
332 Bo::LogicalAnd => "&&",
333 Bo::LogicalOr => "||",
334 Bo::ShiftLeft => "<<",
335 Bo::ShiftRight => ">>",
336 }
337}
338
339impl crate::TypeInner {
340 pub const fn is_handle(&self) -> bool {
342 match *self {
343 Self::Image { .. } | Self::Sampler { .. } | Self::AccelerationStructure { .. } => true,
344 _ => false,
345 }
346 }
347}
348
349impl crate::Statement {
350 pub const fn is_terminator(&self) -> bool {
354 match *self {
355 crate::Statement::Break
356 | crate::Statement::Continue
357 | crate::Statement::Return { .. }
358 | crate::Statement::Kill => true,
359 _ => false,
360 }
361 }
362}
363
364bitflags::bitflags! {
365 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
375 pub struct RayFlag: u32 {
376 const OPAQUE = 0x01;
377 const NO_OPAQUE = 0x02;
378 const TERMINATE_ON_FIRST_HIT = 0x04;
379 const SKIP_CLOSEST_HIT_SHADER = 0x08;
380 const CULL_BACK_FACING = 0x10;
381 const CULL_FRONT_FACING = 0x20;
382 const CULL_OPAQUE = 0x40;
383 const CULL_NO_OPAQUE = 0x80;
384 const SKIP_TRIANGLES = 0x100;
385 const SKIP_AABBS = 0x200;
386 }
387}
388
389#[repr(u32)]
391pub enum RayIntersectionType {
392 Triangle = 1,
393 BoundingBox = 4,
394}
395
396#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
397#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
398#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
399pub struct TaskDispatchLimits {
400 pub max_mesh_workgroups_per_dim: u32,
401 pub max_mesh_workgroups_total: u32,
402}