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
212 = Resource<'static>
213 where
214 'a: 'static;
215
216 fn to_static(&self) -> Self::Static<'static> {
217 Resource {
218 id: self.id,
219 base_type_id: self.base_type_id,
220 type_id: self.type_id,
221 name: CompilerStr::from_string(self.name.to_string()),
222 }
223 }
224}
225
226impl Clone for Resource<'_> {
227 fn clone(&self) -> Resource<'static> {
228 self.to_static()
229 }
230}
231
232#[derive(Debug)]
234pub struct BuiltinResource<'a> {
235 pub builtin: spirv::BuiltIn,
237 pub value_type_id: Handle<TypeId>,
239 pub resource: Resource<'a>,
241}
242
243impl<'a, 'b> From<&'a BuiltinResource<'b>> for Handle<VariableId> {
244 fn from(value: &'a BuiltinResource<'b>) -> Self {
245 value.resource.id
246 }
247}
248
249impl From<BuiltinResource<'_>> for Handle<VariableId> {
250 fn from(value: BuiltinResource<'_>) -> Self {
251 value.resource.id
252 }
253}
254
255impl<'a> BuiltinResource<'a> {
256 fn from_raw(comp: PhantomCompiler, value: &'a spvc_reflected_builtin_resource) -> Option<Self> {
257 let Some(builtin) = spirv::BuiltIn::from_u32(value.builtin.0 as u32) else {
259 if cfg!(debug_assertions) {
260 panic!("Unexpected SpvBuiltIn in spvc_reflected_builtin_resource!")
261 } else {
262 return None;
263 }
264 };
265
266 Some(Self {
267 builtin,
268 value_type_id: comp.create_handle(value.value_type_id),
269 resource: Resource::from_raw(comp, &value.resource),
270 })
271 }
272}
273
274impl Sealed for BuiltinResource<'_> {}
275impl ToStatic for BuiltinResource<'_> {
276 type Static<'a>
277
278 = BuiltinResource<'static>
279 where
280 'a: 'static;
281
282 fn to_static(&self) -> Self::Static<'static> {
283 let resource: BuiltinResource<'static> = BuiltinResource {
284 builtin: self.builtin,
285 value_type_id: self.value_type_id,
286 resource: self.resource.to_static(),
287 };
288 resource
289 }
290}
291
292impl Clone for BuiltinResource<'_> {
293 fn clone(&self) -> BuiltinResource<'static> {
294 self.to_static()
295 }
296}
297
298#[derive(Debug)]
300pub struct AllResources<'a> {
301 pub uniform_buffers: Vec<Resource<'a>>,
303 pub storage_buffers: Vec<Resource<'a>>,
305 pub stage_inputs: Vec<Resource<'a>>,
307 pub stage_outputs: Vec<Resource<'a>>,
309 pub subpass_inputs: Vec<Resource<'a>>,
311 pub storage_images: Vec<Resource<'a>>,
313 pub sampled_images: Vec<Resource<'a>>,
315 pub atomic_counters: Vec<Resource<'a>>,
317 pub acceleration_structures: Vec<Resource<'a>>,
319 pub gl_plain_uniforms: Vec<Resource<'a>>,
321
322 pub push_constant_buffers: Vec<Resource<'a>>,
327 pub shader_record_buffers: Vec<Resource<'a>>,
329
330 pub separate_images: Vec<Resource<'a>>,
332
333 pub separate_samplers: Vec<Resource<'a>>,
335
336 pub builtin_inputs: Vec<BuiltinResource<'a>>,
338 pub builtin_outputs: Vec<BuiltinResource<'a>>,
340}
341
342impl Sealed for AllResources<'_> {}
343impl ToStatic for AllResources<'_> {
344 type Static<'a>
345
346 = AllResources<'static>
347 where
348 'a: 'static;
349
350 fn to_static(&self) -> Self::Static<'static> {
351 AllResources {
352 uniform_buffers: self
353 .uniform_buffers
354 .iter()
355 .map(ToStatic::to_static)
356 .collect(),
357 storage_buffers: self
358 .storage_buffers
359 .iter()
360 .map(ToStatic::to_static)
361 .collect(),
362 stage_inputs: self.stage_inputs.iter().map(ToStatic::to_static).collect(),
363 stage_outputs: self.stage_outputs.iter().map(ToStatic::to_static).collect(),
364 subpass_inputs: self
365 .subpass_inputs
366 .iter()
367 .map(ToStatic::to_static)
368 .collect(),
369 storage_images: self
370 .storage_images
371 .iter()
372 .map(ToStatic::to_static)
373 .collect(),
374 sampled_images: self
375 .sampled_images
376 .iter()
377 .map(ToStatic::to_static)
378 .collect(),
379 atomic_counters: self
380 .atomic_counters
381 .iter()
382 .map(ToStatic::to_static)
383 .collect(),
384 acceleration_structures: self
385 .acceleration_structures
386 .iter()
387 .map(ToStatic::to_static)
388 .collect(),
389 gl_plain_uniforms: self
390 .gl_plain_uniforms
391 .iter()
392 .map(ToStatic::to_static)
393 .collect(),
394 push_constant_buffers: self
395 .push_constant_buffers
396 .iter()
397 .map(ToStatic::to_static)
398 .collect(),
399 shader_record_buffers: self
400 .shader_record_buffers
401 .iter()
402 .map(ToStatic::to_static)
403 .collect(),
404 separate_images: self
405 .separate_images
406 .iter()
407 .map(ToStatic::to_static)
408 .collect(),
409 separate_samplers: self
410 .separate_samplers
411 .iter()
412 .map(ToStatic::to_static)
413 .collect(),
414 builtin_inputs: self
415 .builtin_inputs
416 .iter()
417 .map(ToStatic::to_static)
418 .collect(),
419 builtin_outputs: self
420 .builtin_outputs
421 .iter()
422 .map(ToStatic::to_static)
423 .collect(),
424 }
425 }
426}
427
428impl Clone for AllResources<'_> {
429 fn clone(&self) -> AllResources<'static> {
430 self.to_static()
431 }
432}
433
434impl ShaderResources {
435 pub fn resources_for_type(&self, ty: ResourceType) -> error::Result<ResourceIter<'static>> {
437 let mut count = 0;
442 let mut out = std::ptr::null();
443 unsafe {
444 spirv_cross_sys::spvc_resources_get_resource_list_for_type(
445 self.0.as_ptr(),
446 ty,
447 &mut out,
448 &mut count,
449 )
450 .ok(&self.1)?;
451 }
452
453 let slice = unsafe { slice::from_raw_parts(out, count) };
454
455 Ok(ResourceIter(self.1.clone(), slice.iter()))
456 }
457
458 pub fn builtin_resources_for_type(
460 &self,
461 ty: BuiltinResourceType,
462 ) -> error::Result<BuiltinResourceIter<'static>> {
463 let mut count = 0;
464 let mut out = std::ptr::null();
465
466 unsafe {
471 spirv_cross_sys::spvc_resources_get_builtin_resource_list_for_type(
472 self.0.as_ptr(),
473 ty,
474 &mut out,
475 &mut count,
476 )
477 .ok(&self.1)?;
478 }
479
480 let slice = unsafe { slice::from_raw_parts(out.cast(), count) };
481
482 Ok(BuiltinResourceIter(self.1.clone(), slice.iter()))
483 }
484
485 #[rustfmt::skip]
489 pub fn all_resources(&self) -> error::Result<AllResources<'static>> {
490 Ok(AllResources {
492 uniform_buffers: self.resources_for_type(ResourceType::UniformBuffer)?.collect(),
493 storage_buffers: self.resources_for_type(ResourceType::StorageBuffer)?.collect(),
494 stage_inputs: self.resources_for_type(ResourceType::StageInput)?.collect(),
495 stage_outputs: self.resources_for_type(ResourceType::StageOutput)?.collect(),
496 subpass_inputs: self.resources_for_type(ResourceType::SubpassInput)?.collect(),
497 storage_images: self.resources_for_type(ResourceType::StorageImage)?.collect(),
498 sampled_images: self.resources_for_type(ResourceType::SampledImage)?.collect(),
499 atomic_counters: self.resources_for_type(ResourceType::AtomicCounter)?.collect(),
500 acceleration_structures: self.resources_for_type(ResourceType::AccelerationStructure)?.collect(),
501 gl_plain_uniforms: self.resources_for_type(ResourceType::GlPlainUniform)?.collect(),
502 push_constant_buffers: self.resources_for_type(ResourceType::PushConstant)?.collect(),
503 shader_record_buffers: self.resources_for_type(ResourceType::ShaderRecordBuffer)?.collect(),
504 separate_images: self.resources_for_type(ResourceType::SeparateImage)?.collect(),
505 separate_samplers: self.resources_for_type(ResourceType::SeparateSamplers)?.collect(),
506 builtin_inputs: self.builtin_resources_for_type(BuiltinResourceType::StageInput)?.collect(),
507 builtin_outputs: self.builtin_resources_for_type(BuiltinResourceType::StageOutput)?.collect(),
508 })
509 }
510}