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 str_bytes_to_words(bytes)
19}
20
21pub(super) fn str_bytes_to_words(bytes: &[u8]) -> Vec<Word> {
22 let mut words = bytes_to_words(bytes);
23 if bytes.len().is_multiple_of(4) {
24 words.push(0x0u32);
26 }
27
28 words
29}
30
31#[allow(unstable_name_collisions)]
33pub(super) fn string_to_byte_chunks(input: &str, limit: usize) -> Vec<&[u8]> {
34 let mut offset: usize = 0;
35 let mut start: usize = 0;
36 let mut words = vec![];
37 while offset < input.len() {
38 offset = input.floor_char_boundary_polyfill(offset + limit);
39 #[allow(clippy::sliced_string_as_bytes)]
42 words.push(input[start..offset].as_bytes());
43 start = offset;
44 }
45
46 words
47}
48
49pub(super) const fn map_storage_class(space: crate::AddressSpace) -> spirv::StorageClass {
50 match space {
51 crate::AddressSpace::Handle => spirv::StorageClass::UniformConstant,
52 crate::AddressSpace::Function => spirv::StorageClass::Function,
53 crate::AddressSpace::Private => spirv::StorageClass::Private,
54 crate::AddressSpace::Storage { .. } => spirv::StorageClass::StorageBuffer,
55 crate::AddressSpace::Uniform => spirv::StorageClass::Uniform,
56 crate::AddressSpace::WorkGroup => spirv::StorageClass::Workgroup,
57 crate::AddressSpace::Immediate => spirv::StorageClass::PushConstant,
58 crate::AddressSpace::TaskPayload => spirv::StorageClass::TaskPayloadWorkgroupEXT,
59 crate::AddressSpace::RayPayload => spirv::StorageClass::RayPayloadKHR,
64 crate::AddressSpace::IncomingRayPayload => spirv::StorageClass::IncomingRayPayloadKHR,
65 }
66}
67
68pub(super) fn contains_builtin(
69 binding: Option<&crate::Binding>,
70 ty: Handle<crate::Type>,
71 arena: &UniqueArena<crate::Type>,
72 built_in: crate::BuiltIn,
73) -> bool {
74 if let Some(&crate::Binding::BuiltIn(bi)) = binding {
75 bi == built_in
76 } else if let crate::TypeInner::Struct { ref members, .. } = arena[ty].inner {
77 members
78 .iter()
79 .any(|member| contains_builtin(member.binding.as_ref(), member.ty, arena, built_in))
80 } else {
81 false }
83}
84
85impl crate::AddressSpace {
86 pub(super) const fn to_spirv_semantics_and_scope(
87 self,
88 ) -> (spirv::MemorySemantics, spirv::Scope) {
89 match self {
90 Self::Storage { .. } => (spirv::MemorySemantics::empty(), spirv::Scope::Device),
91 Self::WorkGroup => (spirv::MemorySemantics::empty(), spirv::Scope::Workgroup),
92 Self::Uniform => (spirv::MemorySemantics::empty(), spirv::Scope::Device),
93 Self::Handle => (spirv::MemorySemantics::empty(), spirv::Scope::Device),
94 _ => (spirv::MemorySemantics::empty(), spirv::Scope::Invocation),
95 }
96 }
97}
98
99pub fn global_needs_wrapper(ir_module: &crate::Module, var: &crate::GlobalVariable) -> bool {
105 match var.space {
106 crate::AddressSpace::Uniform
107 | crate::AddressSpace::Storage { .. }
108 | crate::AddressSpace::Immediate => {}
109 _ => return false,
110 };
111 match ir_module.types[var.ty].inner {
112 crate::TypeInner::Struct {
113 ref members,
114 span: _,
115 } => match members.last() {
116 Some(member) => match ir_module.types[member.ty].inner {
117 crate::TypeInner::Array {
119 size: crate::ArraySize::Dynamic,
120 ..
121 } => false,
122 _ => true,
123 },
124 None => false,
125 },
126 crate::TypeInner::BindingArray { .. } => false,
127 _ => true,
129 }
130}
131
132pub fn is_uniform_matcx2_struct_member_access(
135 ir_function: &crate::Function,
136 fun_info: &crate::valid::FunctionInfo,
137 ir_module: &crate::Module,
138 pointer: Handle<crate::Expression>,
139) -> bool {
140 if let crate::TypeInner::Pointer {
141 base: pointer_base_type,
142 space: crate::AddressSpace::Uniform,
143 } = *fun_info[pointer].ty.inner_with(&ir_module.types)
144 {
145 if let crate::TypeInner::Matrix {
146 rows: crate::VectorSize::Bi,
147 ..
148 } = ir_module.types[pointer_base_type].inner
149 {
150 if let crate::Expression::AccessIndex {
151 base: parent_pointer,
152 ..
153 } = ir_function.expressions[pointer]
154 {
155 if let crate::TypeInner::Pointer {
156 base: parent_type, ..
157 } = *fun_info[parent_pointer].ty.inner_with(&ir_module.types)
158 {
159 if let crate::TypeInner::Struct { .. } = ir_module.types[parent_type].inner {
160 return true;
161 }
162 }
163 }
164 }
165 }
166
167 false
168}
169
170trait U8Internal {
173 fn is_utf8_char_boundary_polyfill(&self) -> bool;
174}
175
176impl U8Internal for u8 {
177 fn is_utf8_char_boundary_polyfill(&self) -> bool {
178 (*self as i8) >= -0x40
180 }
181}
182
183trait StrUnstable {
184 fn floor_char_boundary_polyfill(&self, index: usize) -> usize;
185}
186
187impl StrUnstable for str {
188 fn floor_char_boundary_polyfill(&self, index: usize) -> usize {
189 if index >= self.len() {
190 self.len()
191 } else {
192 let lower_bound = index.saturating_sub(3);
193 let new_index = self.as_bytes()[lower_bound..=index]
194 .iter()
195 .rposition(|b| b.is_utf8_char_boundary_polyfill());
196
197 lower_bound + new_index.unwrap()
199 }
200 }
201}
202
203pub enum BindingDecorations {
204 BuiltIn(spirv::BuiltIn, ArrayVec<spirv::Decoration, 2>),
205 Location {
206 location: u32,
207 others: ArrayVec<spirv::Decoration, 5>,
208 blend_src: Option<Word>,
210 },
211 None,
212}