1use alloc::{vec, vec::Vec};
2
3use arrayvec::ArrayVec;
4use spirv::Word;
5
6use crate::{Handle, UniqueArena};
7
8pub(super) fn bytes_to_words(bytes: &[u8]) -> Vec<Word> {
9 bytes
10 .chunks(4)
11 .map(|chars| chars.iter().rev().fold(0u32, |u, c| (u << 8) | *c as u32))
12 .collect()
13}
14
15pub(super) fn string_to_words(input: &str) -> Vec<Word> {
16 let bytes = input.as_bytes();
17
18 debug_str_bytes_to_words(bytes)
19}
20
21pub(super) fn debug_str_bytes_to_words(bytes: &[u8]) -> Vec<Word> {
27 let sanitized;
28 let bytes = if bytes.contains(&0) {
29 sanitized = bytes
30 .iter()
31 .map(|&b| if b == 0 { b'?' } else { b })
32 .collect::<Vec<_>>();
33 &sanitized[..]
34 } else {
35 bytes
36 };
37
38 let mut words = bytes_to_words(bytes);
39 if bytes.len().is_multiple_of(4) {
40 words.push(0x0u32);
42 }
43
44 words
45}
46
47#[allow(unstable_name_collisions)]
49pub(super) fn string_to_byte_chunks(input: &str, limit: usize) -> Vec<&[u8]> {
50 let mut offset: usize = 0;
51 let mut start: usize = 0;
52 let mut words = vec![];
53 while offset < input.len() {
54 offset = input.floor_char_boundary_polyfill(offset + limit);
55 #[allow(clippy::sliced_string_as_bytes)]
58 words.push(input[start..offset].as_bytes());
59 start = offset;
60 }
61
62 words
63}
64
65pub(super) const fn map_storage_class(space: crate::AddressSpace) -> spirv::StorageClass {
66 match space {
67 crate::AddressSpace::Handle => spirv::StorageClass::UniformConstant,
68 crate::AddressSpace::Function => spirv::StorageClass::Function,
69 crate::AddressSpace::Private => spirv::StorageClass::Private,
70 crate::AddressSpace::Storage { .. } => spirv::StorageClass::StorageBuffer,
71 crate::AddressSpace::Uniform => spirv::StorageClass::Uniform,
72 crate::AddressSpace::WorkGroup => spirv::StorageClass::Workgroup,
73 crate::AddressSpace::Immediate => spirv::StorageClass::PushConstant,
74 crate::AddressSpace::TaskPayload => spirv::StorageClass::TaskPayloadWorkgroupEXT,
75 crate::AddressSpace::RayPayload => spirv::StorageClass::RayPayloadKHR,
80 crate::AddressSpace::IncomingRayPayload => spirv::StorageClass::IncomingRayPayloadKHR,
81 }
82}
83
84pub(super) fn contains_builtin(
85 binding: Option<&crate::Binding>,
86 ty: Handle<crate::Type>,
87 arena: &UniqueArena<crate::Type>,
88 built_in: crate::BuiltIn,
89) -> bool {
90 if let Some(&crate::Binding::BuiltIn(bi)) = binding {
91 bi == built_in
92 } else if let crate::TypeInner::Struct { ref members, .. } = arena[ty].inner {
93 members
94 .iter()
95 .any(|member| contains_builtin(member.binding.as_ref(), member.ty, arena, built_in))
96 } else {
97 false }
99}
100
101impl crate::AddressSpace {
102 pub(super) const fn to_spirv_semantics_and_scope(
103 self,
104 ) -> (spirv::MemorySemantics, spirv::Scope) {
105 match self {
106 Self::Storage { .. } => (spirv::MemorySemantics::empty(), spirv::Scope::Device),
107 Self::WorkGroup => (spirv::MemorySemantics::empty(), spirv::Scope::Workgroup),
108 Self::Uniform => (spirv::MemorySemantics::empty(), spirv::Scope::Device),
109 Self::Handle => (spirv::MemorySemantics::empty(), spirv::Scope::Device),
110 _ => (spirv::MemorySemantics::empty(), spirv::Scope::Invocation),
111 }
112 }
113}
114
115pub fn global_needs_wrapper(ir_module: &crate::Module, var: &crate::GlobalVariable) -> bool {
121 match var.space {
122 crate::AddressSpace::Uniform
123 | crate::AddressSpace::Storage { .. }
124 | crate::AddressSpace::Immediate => {}
125 _ => return false,
126 };
127 match ir_module.types[var.ty].inner {
128 crate::TypeInner::Struct {
129 ref members,
130 span: _,
131 } => match members.last() {
132 Some(member) => match ir_module.types[member.ty].inner {
133 crate::TypeInner::Array {
135 size: crate::ArraySize::Dynamic,
136 ..
137 } => false,
138 _ => true,
139 },
140 None => false,
141 },
142 crate::TypeInner::BindingArray { .. } => false,
143 _ => true,
145 }
146}
147
148pub fn is_uniform_matcx2_struct_member_access(
151 ir_function: &crate::Function,
152 fun_info: &crate::valid::FunctionInfo,
153 ir_module: &crate::Module,
154 pointer: Handle<crate::Expression>,
155) -> bool {
156 if let crate::TypeInner::Pointer {
157 base: pointer_base_type,
158 space: crate::AddressSpace::Uniform,
159 } = *fun_info[pointer].ty.inner_with(&ir_module.types)
160 {
161 if let crate::TypeInner::Matrix {
162 rows: crate::VectorSize::Bi,
163 ..
164 } = ir_module.types[pointer_base_type].inner
165 {
166 if let crate::Expression::AccessIndex {
167 base: parent_pointer,
168 ..
169 } = ir_function.expressions[pointer]
170 {
171 if let crate::TypeInner::Pointer {
172 base: parent_type, ..
173 } = *fun_info[parent_pointer].ty.inner_with(&ir_module.types)
174 {
175 if let crate::TypeInner::Struct { .. } = ir_module.types[parent_type].inner {
176 return true;
177 }
178 }
179 }
180 }
181 }
182
183 false
184}
185
186trait U8Internal {
189 fn is_utf8_char_boundary_polyfill(&self) -> bool;
190}
191
192impl U8Internal for u8 {
193 fn is_utf8_char_boundary_polyfill(&self) -> bool {
194 (*self as i8) >= -0x40
196 }
197}
198
199trait StrUnstable {
200 fn floor_char_boundary_polyfill(&self, index: usize) -> usize;
201}
202
203impl StrUnstable for str {
204 fn floor_char_boundary_polyfill(&self, index: usize) -> usize {
205 if index >= self.len() {
206 self.len()
207 } else {
208 let lower_bound = index.saturating_sub(3);
209 let new_index = self.as_bytes()[lower_bound..=index]
210 .iter()
211 .rposition(|b| b.is_utf8_char_boundary_polyfill());
212
213 lower_bound + new_index.unwrap()
215 }
216 }
217}
218
219#[derive(Debug)]
220pub enum BindingDecorations {
221 BuiltIn(spirv::BuiltIn, ArrayVec<spirv::Decoration, 2>),
222 Location {
223 location: u32,
224 others: ArrayVec<spirv::Decoration, 5>,
225 blend_src: Option<Word>,
227 },
228 None,
229}