1use crate::error::{SpirvCrossError, ToContextError};
2use crate::handle::{Handle, TypeId, VariableId};
3use crate::sealed::Sealed;
4use crate::string::CompilerStr;
5use crate::{error, Compiler, PhantomCompiler, ToStatic};
6use spirv_cross_sys as sys;
7use spirv_cross_sys::{
8 spvc_reflected_builtin_resource, spvc_reflected_resource, spvc_resources_s, spvc_set,
9};
10use std::ptr::NonNull;
11use std::slice;
12
13pub use spirv_cross_sys::BuiltinResourceType;
15
16use crate::iter::impl_iterator;
17pub use spirv_cross_sys::ResourceType;
19
20pub struct ShaderResources(NonNull<spvc_resources_s>, PhantomCompiler);
22
23impl<T> Compiler<T> {
24 pub fn shader_resources(&self) -> crate::error::Result<ShaderResources> {
26 unsafe {
30 let mut resources = std::ptr::null_mut();
31 sys::spvc_compiler_create_shader_resources(self.ptr.as_ptr(), &mut resources)
32 .ok(self)?;
33
34 let Some(resources) = NonNull::new(resources) else {
35 return Err(SpirvCrossError::OutOfMemory(String::from("Out of memory")));
36 };
37
38 Ok(ShaderResources(resources, self.phantom()))
39 }
40 }
41
42 pub fn shader_resources_for_active_variables(
46 &self,
47 set: InterfaceVariableSet,
48 ) -> error::Result<ShaderResources> {
49 unsafe {
53 let mut resources = std::ptr::null_mut();
54 sys::spvc_compiler_create_shader_resources_for_active_variables(
55 self.ptr.as_ptr(),
56 &mut resources,
57 set.0,
58 )
59 .ok(self)?;
60
61 let Some(resources) = NonNull::new(resources) else {
62 return Err(SpirvCrossError::OutOfMemory(String::from("Out of memory")));
63 };
64
65 Ok(ShaderResources(resources, self.phantom()))
66 }
67 }
68}
69
70pub struct InterfaceVariableSet(spvc_set, Handle<()>, PhantomCompiler);
72
73impl InterfaceVariableSet {
74 pub fn to_handles(&self) -> Vec<Handle<VariableId>> {
79 unsafe {
80 let mut length = 0;
82 spirv_cross_sys::spvc_rs_expose_set(self.0, std::ptr::null_mut(), &mut length);
83
84 let mut vec = vec![0; length];
86 spirv_cross_sys::spvc_rs_expose_set(self.0, vec.as_mut_ptr(), &mut length);
87
88 let mut handles: Vec<Handle<VariableId>> = vec
89 .into_iter()
90 .map(|id| self.2.create_handle(VariableId::from(id)))
91 .collect();
92
93 handles.sort_by_key(|h| h.id());
94
95 handles
96 }
97 }
98}
99
100impl<T> Compiler<T> {
102 pub fn active_interface_variables(&self) -> error::Result<InterfaceVariableSet> {
114 unsafe {
115 let mut set = std::ptr::null();
116
117 sys::spvc_compiler_get_active_interface_variables(self.ptr.as_ptr(), &mut set)
121 .ok(self)?;
122
123 Ok(InterfaceVariableSet(
124 set,
125 self.create_handle(()),
126 self.phantom(),
127 ))
128 }
129 }
130
131 pub fn set_enabled_interface_variables(
135 &mut self,
136 set: InterfaceVariableSet,
137 ) -> error::Result<()> {
138 if !self.handle_is_valid(&set.1) {
139 return Err(SpirvCrossError::InvalidOperation(String::from(
140 "The interface variable set is invalid for this compiler instance.",
141 )));
142 }
143 unsafe {
144 sys::spvc_compiler_set_enabled_interface_variables(self.ptr.as_ptr(), set.0)
145 .ok(&*self)?;
146 Ok(())
147 }
148 }
149}
150
151pub struct ResourceIter<'a>(PhantomCompiler, slice::Iter<'a, spvc_reflected_resource>);
153
154impl_iterator!(ResourceIter<'a>: Resource<'a> as map |s, o: &'a spvc_reflected_resource| {
155 Resource::from_raw(s.0.clone(), o)
156} for <'a> [1]);
157
158pub struct BuiltinResourceIter<'a>(
160 PhantomCompiler,
161 slice::Iter<'a, spvc_reflected_builtin_resource>,
162);
163
164impl_iterator!(BuiltinResourceIter<'a>: BuiltinResource<'a> as and_then |s, o: &'a spvc_reflected_builtin_resource| {
165 BuiltinResource::from_raw(s.0.clone(), o)
166} for <'a> [1]);
167
168#[derive(Debug)]
170pub struct Resource<'a> {
171 pub id: Handle<VariableId>,
173 pub base_type_id: Handle<TypeId>,
175 pub type_id: Handle<TypeId>,
178 pub name: CompilerStr<'a>,
180}
181
182impl<'a> Resource<'a> {
183 fn from_raw(comp: PhantomCompiler, value: &'a spvc_reflected_resource) -> Self {
184 Self {
185 id: comp.create_handle(value.id),
186 base_type_id: comp.create_handle(value.base_type_id),
187 type_id: comp.create_handle(value.type_id),
188 name: unsafe { CompilerStr::from_ptr(value.name, comp.ctx.clone()) },
192 }
193 }
194}
195
196impl<'a, 'b> From<&'a Resource<'b>> for Handle<VariableId> {
197 fn from(value: &'a Resource<'b>) -> Self {
198 value.id
199 }
200}
201
202impl From<Resource<'_>> for Handle<VariableId> {
203 fn from(value: Resource<'_>) -> Self {
204 value.id
205 }
206}
207
208impl Sealed for Resource<'_> {}
209impl ToStatic for Resource<'_> {
210 type Static<'a>
211 = Resource<'static>
212 where
213 'a: 'static;
214
215 fn to_static(&self) -> Self::Static<'static> {
216 Resource {
217 id: self.id,
218 base_type_id: self.base_type_id,
219 type_id: self.type_id,
220 name: CompilerStr::from_string(self.name.to_string()),
221 }
222 }
223}
224
225impl Clone for Resource<'_> {
226 fn clone(&self) -> Resource<'static> {
227 self.to_static()
228 }
229}
230
231#[derive(Debug)]
233pub struct BuiltinResource<'a> {
234 pub builtin: spirv::BuiltIn,
236 pub value_type_id: Handle<TypeId>,
238 pub resource: Resource<'a>,
240}
241
242impl<'a, 'b> From<&'a BuiltinResource<'b>> for Handle<VariableId> {
243 fn from(value: &'a BuiltinResource<'b>) -> Self {
244 value.resource.id
245 }
246}
247
248impl From<BuiltinResource<'_>> for Handle<VariableId> {
249 fn from(value: BuiltinResource<'_>) -> Self {
250 value.resource.id
251 }
252}
253
254impl<'a> BuiltinResource<'a> {
255 fn from_raw(comp: PhantomCompiler, value: &'a spvc_reflected_builtin_resource) -> Option<Self> {
256 let Some(builtin) = spirv::BuiltIn::from_u32(value.builtin.0 as u32) else {
258 if cfg!(debug_assertions) {
259 panic!("Unexpected SpvBuiltIn in spvc_reflected_builtin_resource!")
260 } else {
261 return None;
262 }
263 };
264
265 Some(Self {
266 builtin,
267 value_type_id: comp.create_handle(value.value_type_id),
268 resource: Resource::from_raw(comp, &value.resource),
269 })
270 }
271}
272
273impl Sealed for BuiltinResource<'_> {}
274impl ToStatic for BuiltinResource<'_> {
275 type Static<'a>
276 = BuiltinResource<'static>
277 where
278 'a: 'static;
279
280 fn to_static(&self) -> Self::Static<'static> {
281 let resource: BuiltinResource<'static> = BuiltinResource {
282 builtin: self.builtin,
283 value_type_id: self.value_type_id,
284 resource: self.resource.to_static(),
285 };
286 resource
287 }
288}
289
290impl Clone for BuiltinResource<'_> {
291 fn clone(&self) -> BuiltinResource<'static> {
292 self.to_static()
293 }
294}
295
296#[derive(Debug)]
298pub struct AllResources<'a> {
299 pub uniform_buffers: Vec<Resource<'a>>,
301 pub storage_buffers: Vec<Resource<'a>>,
303 pub stage_inputs: Vec<Resource<'a>>,
305 pub stage_outputs: Vec<Resource<'a>>,
307 pub subpass_inputs: Vec<Resource<'a>>,
309 pub storage_images: Vec<Resource<'a>>,
311 pub sampled_images: Vec<Resource<'a>>,
313 pub atomic_counters: Vec<Resource<'a>>,
315 pub acceleration_structures: Vec<Resource<'a>>,
317 pub gl_plain_uniforms: Vec<Resource<'a>>,
319
320 pub push_constant_buffers: Vec<Resource<'a>>,
325 pub shader_record_buffers: Vec<Resource<'a>>,
327
328 pub separate_images: Vec<Resource<'a>>,
330
331 pub separate_samplers: Vec<Resource<'a>>,
333
334 pub builtin_inputs: Vec<BuiltinResource<'a>>,
336 pub builtin_outputs: Vec<BuiltinResource<'a>>,
338}
339
340impl Sealed for AllResources<'_> {}
341impl ToStatic for AllResources<'_> {
342 type Static<'a>
343 = AllResources<'static>
344 where
345 'a: 'static;
346
347 fn to_static(&self) -> Self::Static<'static> {
348 AllResources {
349 uniform_buffers: self
350 .uniform_buffers
351 .iter()
352 .map(ToStatic::to_static)
353 .collect(),
354 storage_buffers: self
355 .storage_buffers
356 .iter()
357 .map(ToStatic::to_static)
358 .collect(),
359 stage_inputs: self.stage_inputs.iter().map(ToStatic::to_static).collect(),
360 stage_outputs: self.stage_outputs.iter().map(ToStatic::to_static).collect(),
361 subpass_inputs: self
362 .subpass_inputs
363 .iter()
364 .map(ToStatic::to_static)
365 .collect(),
366 storage_images: self
367 .storage_images
368 .iter()
369 .map(ToStatic::to_static)
370 .collect(),
371 sampled_images: self
372 .sampled_images
373 .iter()
374 .map(ToStatic::to_static)
375 .collect(),
376 atomic_counters: self
377 .atomic_counters
378 .iter()
379 .map(ToStatic::to_static)
380 .collect(),
381 acceleration_structures: self
382 .acceleration_structures
383 .iter()
384 .map(ToStatic::to_static)
385 .collect(),
386 gl_plain_uniforms: self
387 .gl_plain_uniforms
388 .iter()
389 .map(ToStatic::to_static)
390 .collect(),
391 push_constant_buffers: self
392 .push_constant_buffers
393 .iter()
394 .map(ToStatic::to_static)
395 .collect(),
396 shader_record_buffers: self
397 .shader_record_buffers
398 .iter()
399 .map(ToStatic::to_static)
400 .collect(),
401 separate_images: self
402 .separate_images
403 .iter()
404 .map(ToStatic::to_static)
405 .collect(),
406 separate_samplers: self
407 .separate_samplers
408 .iter()
409 .map(ToStatic::to_static)
410 .collect(),
411 builtin_inputs: self
412 .builtin_inputs
413 .iter()
414 .map(ToStatic::to_static)
415 .collect(),
416 builtin_outputs: self
417 .builtin_outputs
418 .iter()
419 .map(ToStatic::to_static)
420 .collect(),
421 }
422 }
423}
424
425impl Clone for AllResources<'_> {
426 fn clone(&self) -> AllResources<'static> {
427 self.to_static()
428 }
429}
430
431impl ShaderResources {
432 pub fn resources_for_type(&self, ty: ResourceType) -> error::Result<ResourceIter<'static>> {
434 let mut count = 0;
439 let mut out = std::ptr::null();
440 unsafe {
441 spirv_cross_sys::spvc_resources_get_resource_list_for_type(
442 self.0.as_ptr(),
443 ty,
444 &mut out,
445 &mut count,
446 )
447 .ok(&self.1)?;
448 }
449
450 let slice = unsafe { slice::from_raw_parts(out, count) };
451
452 Ok(ResourceIter(self.1.clone(), slice.iter()))
453 }
454
455 pub fn builtin_resources_for_type(
457 &self,
458 ty: BuiltinResourceType,
459 ) -> error::Result<BuiltinResourceIter<'static>> {
460 let mut count = 0;
461 let mut out = std::ptr::null();
462
463 unsafe {
468 spirv_cross_sys::spvc_resources_get_builtin_resource_list_for_type(
469 self.0.as_ptr(),
470 ty,
471 &mut out,
472 &mut count,
473 )
474 .ok(&self.1)?;
475 }
476
477 let slice = unsafe { slice::from_raw_parts(out.cast(), count) };
478
479 Ok(BuiltinResourceIter(self.1.clone(), slice.iter()))
480 }
481
482 #[rustfmt::skip]
486 pub fn all_resources(&self) -> error::Result<AllResources<'static>> {
487 Ok(AllResources {
489 uniform_buffers: self.resources_for_type(ResourceType::UniformBuffer)?.collect(),
490 storage_buffers: self.resources_for_type(ResourceType::StorageBuffer)?.collect(),
491 stage_inputs: self.resources_for_type(ResourceType::StageInput)?.collect(),
492 stage_outputs: self.resources_for_type(ResourceType::StageOutput)?.collect(),
493 subpass_inputs: self.resources_for_type(ResourceType::SubpassInput)?.collect(),
494 storage_images: self.resources_for_type(ResourceType::StorageImage)?.collect(),
495 sampled_images: self.resources_for_type(ResourceType::SampledImage)?.collect(),
496 atomic_counters: self.resources_for_type(ResourceType::AtomicCounter)?.collect(),
497 acceleration_structures: self.resources_for_type(ResourceType::AccelerationStructure)?.collect(),
498 gl_plain_uniforms: self.resources_for_type(ResourceType::GlPlainUniform)?.collect(),
499 push_constant_buffers: self.resources_for_type(ResourceType::PushConstant)?.collect(),
500 shader_record_buffers: self.resources_for_type(ResourceType::ShaderRecordBuffer)?.collect(),
501 separate_images: self.resources_for_type(ResourceType::SeparateImage)?.collect(),
502 separate_samplers: self.resources_for_type(ResourceType::SeparateSamplers)?.collect(),
503 builtin_inputs: self.builtin_resources_for_type(BuiltinResourceType::StageInput)?.collect(),
504 builtin_outputs: self.builtin_resources_for_type(BuiltinResourceType::StageOutput)?.collect(),
505 })
506 }
507}