spirv_cross2/reflect/
combined_image_samplers.rs

1use crate::error::{SpirvCrossError, ToContextError};
2use crate::handle::{Handle, VariableId};
3use crate::iter::impl_iterator;
4use crate::{error, Compiler, PhantomCompiler};
5use spirv_cross_sys as sys;
6use std::slice;
7
8/// A proof that [`Compiler::create_dummy_sampler_for_combined_images`] was called.
9#[derive(Debug, Copy, Clone)]
10pub struct BuiltDummySamplerProof {
11    /// The handle to a sampler object, if one was needed to be created.
12    pub sampler_id: Option<Handle<VariableId>>,
13    label: Handle<()>,
14}
15
16/// Iterator for [`CombinedImageSampler`].
17pub struct CombinedImageSamplerIter<'a>(
18    PhantomCompiler,
19    slice::Iter<'a, sys::spvc_combined_image_sampler>,
20);
21
22impl_iterator!(CombinedImageSamplerIter<'_>: CombinedImageSampler
23    as map |s, c: &sys::spvc_combined_image_sampler| {
24        let combined_id = s.0.create_handle(c.combined_id);
25            let image_id = s.0.create_handle(c.image_id);
26            let sampler_id = s.0.create_handle(c.sampler_id);
27
28            CombinedImageSampler {
29                combined_id,
30                image_id,
31                sampler_id,
32            }
33} for [1]);
34
35/// A combined image sampler.
36pub struct CombinedImageSampler {
37    /// A handle to the created combined image sampler.
38    pub combined_id: Handle<VariableId>,
39    /// A handle to the split image of the combined image sampler.
40    pub image_id: Handle<VariableId>,
41    /// A handle to the split sampler of the combined image sampler.
42    pub sampler_id: Handle<VariableId>,
43}
44
45impl<T> Compiler<T> {
46    /// Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where
47    /// said instruction is used without a combined image sampler.
48    /// GLSL targets do not support the use of texelFetch without a sampler.
49    /// To work around this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of
50    /// texelFetch as necessary.
51    ///
52    /// This must be called to obtain a proof to call [`Compiler::build_combined_image_samplers`].
53    ///
54    /// The proof contains the ID of a sampler object, if one dummy sampler is necessary. This ID can
55    /// be decorated with set/bindings as desired before compiling.
56    pub fn create_dummy_sampler_for_combined_images(
57        &mut self,
58    ) -> error::Result<BuiltDummySamplerProof> {
59        unsafe {
60            let mut var_id = VariableId::from(0);
61            sys::spvc_compiler_build_dummy_sampler_for_combined_images(
62                self.ptr.as_ptr(),
63                &mut var_id,
64            )
65            .ok(&*self)?;
66
67            let sampler_id = self.create_handle_if_not_zero(var_id);
68
69            Ok(BuiltDummySamplerProof {
70                sampler_id,
71                label: self.create_handle(()),
72            })
73        }
74    }
75
76    /// Analyzes all separate image and samplers used from the currently selected entry point,
77    /// and re-routes them all to a combined image sampler instead.
78    /// This is required to "support" separate image samplers in targets which do not natively support
79    /// this feature, like GLSL/ESSL.
80    ///
81    /// This call will add new sampled images to the SPIR-V,
82    /// so it will appear in reflection if [`Compiler::shader_resources`] is called after.
83    ///
84    /// If any image/sampler remapping was found, no separate image/samplers will appear in the decompiled output,
85    /// but will still appear in reflection.
86    ///
87    /// The resulting samplers will be void of any decorations like name, descriptor sets and binding points,
88    /// so this can be added before compilation if desired.
89    ///
90    /// Combined image samplers originating from this set are always considered active variables.
91    /// Arrays of separate samplers are not supported, but arrays of separate images are supported.
92    /// Array of images + sampler -> Array of combined image samplers.
93    ///
94    /// [`Compiler::create_dummy_sampler_for_combined_images`] must be called before this to obtain
95    /// a proof that a dummy sampler, if necessary, was created. Passing in a smuggled proof from
96    /// a different compiler instance will result in an error.
97    pub fn build_combined_image_samplers(
98        &mut self,
99        proof: BuiltDummySamplerProof,
100    ) -> error::Result<()> {
101        // check for smuggling
102        if !self.handle_is_valid(&proof.label) {
103            return Err(SpirvCrossError::InvalidOperation(String::from(
104                "The provided proof of building combined image samplers is invalid",
105            )));
106        }
107
108        unsafe {
109            sys::spvc_compiler_build_combined_image_samplers(self.ptr.as_ptr()).ok(&*self)?;
110
111            Ok(())
112        }
113    }
114
115    /// Gets a remapping for the combined image samplers.
116    pub fn combined_image_samplers(&self) -> error::Result<CombinedImageSamplerIter<'static>> {
117        unsafe {
118            let mut samplers = std::ptr::null();
119            let mut size = 0;
120
121            // SAFETY: 'ctx is sound here.
122            // https://github.com/KhronosGroup/SPIRV-Cross/blob/main/spirv_cross_c.cpp#L2497
123            sys::spvc_compiler_get_combined_image_samplers(
124                self.ptr.as_ptr(),
125                &mut samplers,
126                &mut size,
127            )
128            .ok(self)?;
129            let slice = slice::from_raw_parts(samplers, size);
130            Ok(CombinedImageSamplerIter(self.phantom(), slice.iter()))
131        }
132    }
133}
134
135#[cfg(test)]
136mod test {
137    use crate::error::SpirvCrossError;
138    use crate::Compiler;
139    use crate::{targets, Module};
140
141    static BASIC_SPV: &[u8] = include_bytes!("../../basic.spv");
142
143    #[test]
144    pub fn test_combined_image_sampler_build() -> Result<(), SpirvCrossError> {
145        let vec = Vec::from(BASIC_SPV);
146        let words = Module::from_words(bytemuck::cast_slice(&vec));
147
148        let mut compiler: Compiler<targets::None> = Compiler::new(words)?;
149
150        let proof = compiler.create_dummy_sampler_for_combined_images()?;
151        compiler.build_combined_image_samplers(proof)?;
152
153        // match ty.inner {
154        //     TypeInner::Struct(ty) => {
155        //         compiler.get_type(ty.members[0].id)?;
156        //     }
157        //     TypeInner::Vector { .. } => {}
158        //     _ => {}
159        // }
160        Ok(())
161    }
162}