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
41struct Baked(crate::Handle<crate::Expression>);
50
51impl core::fmt::Display for Baked {
52 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53 self.0.write_prefixed(f, "_e")
54 }
55}
56
57pub type PipelineConstants = hashbrown::HashMap<String, f64>;
65
66#[derive(Clone, Copy)]
68pub struct Level(pub usize);
69
70impl Level {
71 pub const fn next(&self) -> Self {
72 Level(self.0 + 1)
73 }
74}
75
76impl core::fmt::Display for Level {
77 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
78 (0..self.0).try_for_each(|_| formatter.write_str(INDENT))
79 }
80}
81
82pub enum FunctionType {
98 Function(crate::Handle<crate::Function>),
100 EntryPoint(crate::proc::EntryPointIndex),
105}
106
107impl FunctionType {
108 pub fn is_compute_entry_point(&self, module: &crate::Module) -> bool {
110 match *self {
111 FunctionType::EntryPoint(index) => {
112 module.entry_points[index as usize].stage == crate::ShaderStage::Compute
113 }
114 FunctionType::Function(_) => false,
115 }
116 }
117}
118
119pub struct FunctionCtx<'a> {
121 pub ty: FunctionType,
123 pub info: &'a crate::valid::FunctionInfo,
125 pub expressions: &'a crate::Arena<crate::Expression>,
127 pub named_expressions: &'a crate::NamedExpressions,
129}
130
131impl FunctionCtx<'_> {
132 pub fn resolve_type<'a>(
134 &'a self,
135 handle: crate::Handle<crate::Expression>,
136 types: &'a crate::UniqueArena<crate::Type>,
137 ) -> &'a crate::TypeInner {
138 self.info[handle].ty.inner_with(types)
139 }
140
141 pub const fn name_key(
143 &self,
144 local: crate::Handle<crate::LocalVariable>,
145 ) -> crate::proc::NameKey {
146 match self.ty {
147 FunctionType::Function(handle) => crate::proc::NameKey::FunctionLocal(handle, local),
148 FunctionType::EntryPoint(idx) => crate::proc::NameKey::EntryPointLocal(idx, local),
149 }
150 }
151
152 pub const fn argument_key(&self, arg: u32) -> crate::proc::NameKey {
157 match self.ty {
158 FunctionType::Function(handle) => crate::proc::NameKey::FunctionArgument(handle, arg),
159 FunctionType::EntryPoint(ep_index) => {
160 crate::proc::NameKey::EntryPointArgument(ep_index, arg)
161 }
162 }
163 }
164
165 pub fn is_fixed_function_input(
167 &self,
168 mut expression: crate::Handle<crate::Expression>,
169 module: &crate::Module,
170 ) -> Option<crate::BuiltIn> {
171 let ep_function = match self.ty {
172 FunctionType::Function(_) => return None,
173 FunctionType::EntryPoint(ep_index) => &module.entry_points[ep_index as usize].function,
174 };
175 let mut built_in = None;
176 loop {
177 match self.expressions[expression] {
178 crate::Expression::FunctionArgument(arg_index) => {
179 return match ep_function.arguments[arg_index as usize].binding {
180 Some(crate::Binding::BuiltIn(bi)) => Some(bi),
181 _ => built_in,
182 };
183 }
184 crate::Expression::AccessIndex { base, index } => {
185 match *self.resolve_type(base, &module.types) {
186 crate::TypeInner::Struct { ref members, .. } => {
187 if let Some(crate::Binding::BuiltIn(bi)) =
188 members[index as usize].binding
189 {
190 built_in = Some(bi);
191 }
192 }
193 _ => return None,
194 }
195 expression = base;
196 }
197 _ => return None,
198 }
199 }
200 }
201}
202
203impl crate::Expression {
204 pub const fn bake_ref_count(&self) -> usize {
213 match *self {
214 crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => usize::MAX,
216 crate::Expression::ImageSample { .. } | crate::Expression::ImageLoad { .. } => 1,
218 crate::Expression::Derivative { .. } => 1,
220 crate::Expression::Load { .. } => 1,
224 _ => 2,
226 }
227 }
228}
229
230pub const fn binary_operation_str(op: crate::BinaryOperator) -> &'static str {
232 use crate::BinaryOperator as Bo;
233 match op {
234 Bo::Add => "+",
235 Bo::Subtract => "-",
236 Bo::Multiply => "*",
237 Bo::Divide => "/",
238 Bo::Modulo => "%",
239 Bo::Equal => "==",
240 Bo::NotEqual => "!=",
241 Bo::Less => "<",
242 Bo::LessEqual => "<=",
243 Bo::Greater => ">",
244 Bo::GreaterEqual => ">=",
245 Bo::And => "&",
246 Bo::ExclusiveOr => "^",
247 Bo::InclusiveOr => "|",
248 Bo::LogicalAnd => "&&",
249 Bo::LogicalOr => "||",
250 Bo::ShiftLeft => "<<",
251 Bo::ShiftRight => ">>",
252 }
253}
254
255impl crate::TypeInner {
256 pub const fn is_handle(&self) -> bool {
258 match *self {
259 crate::TypeInner::Image { .. }
260 | crate::TypeInner::Sampler { .. }
261 | crate::TypeInner::AccelerationStructure { .. } => true,
262 _ => false,
263 }
264 }
265}
266
267impl crate::Statement {
268 pub const fn is_terminator(&self) -> bool {
272 match *self {
273 crate::Statement::Break
274 | crate::Statement::Continue
275 | crate::Statement::Return { .. }
276 | crate::Statement::Kill => true,
277 _ => false,
278 }
279 }
280}
281
282bitflags::bitflags! {
283 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
293 pub struct RayFlag: u32 {
294 const OPAQUE = 0x01;
295 const NO_OPAQUE = 0x02;
296 const TERMINATE_ON_FIRST_HIT = 0x04;
297 const SKIP_CLOSEST_HIT_SHADER = 0x08;
298 const CULL_BACK_FACING = 0x10;
299 const CULL_FRONT_FACING = 0x20;
300 const CULL_OPAQUE = 0x40;
301 const CULL_NO_OPAQUE = 0x80;
302 const SKIP_TRIANGLES = 0x100;
303 const SKIP_AABBS = 0x200;
304 }
305}
306
307#[repr(u32)]
309pub enum RayIntersectionType {
310 Triangle = 1,
311 BoundingBox = 4,
312}