spirv_cross2/compile/glsl/
mod.rs

1use super::CommonOptions;
2use crate::compile::sealed::ApplyCompilerOptions;
3use crate::error::ToContextError;
4use crate::handle::Handle;
5use crate::iter::impl_iterator;
6use crate::sealed::Sealed;
7use crate::targets::Glsl;
8use crate::{error, Compiler, CompilerStr, ContextRooted, PhantomCompiler};
9use spirv_cross_sys as sys;
10use spirv_cross_sys::{spvc_compiler_option, spvc_compiler_options, VariableId};
11use std::marker::PhantomData;
12use std::ops::Range;
13
14impl Sealed for CompilerOptions {}
15/// GLSL compiler options.
16#[non_exhaustive]
17#[derive(Debug, spirv_cross2_derive::CompilerOptions)]
18pub struct CompilerOptions {
19    /// Compile options common to GLSL, HLSL, and MSL.
20    #[expand]
21    pub common: CommonOptions,
22
23    /// The GLSL version to output. The default is #version 450.
24    #[expand]
25    pub version: GlslVersion,
26
27    /// If true, Vulkan GLSL features are used instead of GL-compatible features.
28    /// Mostly useful for debugging SPIR-V files.
29    #[option(SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, false)]
30    pub vulkan_semantics: bool,
31
32    /// If true, gl_PerVertex is explicitly redeclared in vertex, geometry and tessellation shaders.
33    /// The members of gl_PerVertex is determined by which built-ins are declared by the shader.
34    /// This option is ignored in ES versions, as redeclaration in ES is not required, and it depends on a different extension
35    /// (EXT_shader_io_blocks) which makes things a bit more fuzzy.
36    #[option(SPVC_COMPILER_OPTION_GLSL_SEPARATE_SHADER_OBJECTS, false)]
37    pub seperate_shader_objects: bool,
38
39    /// For older desktop GLSL targets than version 420, the
40    /// GL_ARB_shading_language_420pack extensions is used to be able to support
41    /// layout(binding) on UBOs and samplers.
42    /// If disabled on older targets, binding decorations will be stripped.
43    ///
44    /// The default is true.
45    #[option(SPVC_COMPILER_OPTION_GLSL_ENABLE_420PACK_EXTENSION, true)]
46    pub enable_420pack_extension: bool,
47
48    /// If true, the backend will assume that InstanceIndex will need to apply
49    /// a base instance offset. Set to false if you know you will never use base instance
50    /// functionality as it might remove some internal uniforms.
51    #[option(SPVC_COMPILER_OPTION_GLSL_SUPPORT_NONZERO_BASE_INSTANCE, true)]
52    pub support_nonzero_base_instance: bool,
53
54    /// If true, sets the default float precision in ES targets to highp,
55    /// otherwise the default is mediump.
56    #[option(SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_FLOAT_PRECISION_HIGHP, false)]
57    pub es_default_float_precision_highp: bool,
58
59    /// If false, sets the default float precision in ES targets to mediump,
60    /// otherwise the default is highp.
61    #[option(SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_INT_PRECISION_HIGHP, true)]
62    pub es_default_int_precision_highp: bool,
63
64    /// In non-Vulkan GLSL, emit push constant blocks as UBOs rather than plain uniforms.
65    #[option(SPVC_COMPILER_OPTION_GLSL_EMIT_PUSH_CONSTANT_AS_UNIFORM_BUFFER, false)]
66    pub emit_push_constant_as_uniform_buffer: bool,
67
68    /// Always emit uniform blocks as plain uniforms, regardless of the GLSL version, even when UBOs are supported.
69    /// Does not apply to shader storage or push constant blocks.
70    #[option(SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS, false)]
71    pub emit_uniform_buffer_as_plain_uniforms: bool,
72
73    /// In GLSL, force use of I/O block flattening, similar to
74    /// what happens on legacy GLSL targets for blocks and structs.
75    #[option(SPVC_COMPILER_OPTION_GLSL_FORCE_FLATTENED_IO_BLOCKS, false)]
76    pub force_flattened_io_blocks: bool,
77
78    /// Loading row-major matrices from UBOs on older AMD Windows OpenGL drivers is problematic.
79    /// To load these types correctly, we must generate a wrapper. them in a dummy function which only purpose is to
80    /// ensure row_major decoration is actually respected.
81    /// This workaround may cause significant performance degeneration on some Android devices.
82    #[option(SPVC_COMPILER_OPTION_GLSL_ENABLE_ROW_MAJOR_LOAD_WORKAROUND, true)]
83    pub enable_row_major_load_workaround: bool,
84
85    /// If non-zero, controls `layout(num_views = N) in;` in GL_OVR_multiview2.
86    #[option(SPVC_COMPILER_OPTION_GLSL_OVR_MULTIVIEW_VIEW_COUNT, 0)]
87    pub ovr_multiview_view_count: u32,
88}
89
90impl Sealed for GlslVersion {}
91
92/// GLSL language version.
93#[non_exhaustive]
94#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
95pub enum GlslVersion {
96    /// #version 110
97    Glsl110,
98    /// #version 120
99    Glsl120,
100    /// #version 130
101    Glsl130,
102    /// #version 140
103    Glsl140,
104    /// #version 150
105    Glsl150,
106    /// #version 330
107    Glsl330,
108    /// #version 400
109    Glsl400,
110    /// #version 410
111    Glsl410,
112    /// #version 420
113    Glsl420,
114    /// #version 430
115    Glsl430,
116    /// #version 440
117    Glsl440,
118    /// #version 450
119    #[default]
120    Glsl450,
121    /// #version 460
122    Glsl460,
123    /// #version 100 es
124    Glsl100Es,
125    /// #version 300 es
126    Glsl300Es,
127    /// #version 310 es
128    Glsl310Es,
129    /// #version 320 es
130    Glsl320Es,
131}
132
133impl ApplyCompilerOptions for GlslVersion {
134    unsafe fn apply(
135        &self,
136        options: spvc_compiler_options,
137        root: impl ContextRooted + Copy,
138    ) -> error::Result<()> {
139        let version = match self {
140            GlslVersion::Glsl110 => 110,
141            GlslVersion::Glsl120 => 120,
142            GlslVersion::Glsl130 => 130,
143            GlslVersion::Glsl140 => 140,
144            GlslVersion::Glsl150 => 150,
145            GlslVersion::Glsl330 => 330,
146            GlslVersion::Glsl400 => 400,
147            GlslVersion::Glsl410 => 410,
148            GlslVersion::Glsl420 => 420,
149            GlslVersion::Glsl430 => 430,
150            GlslVersion::Glsl440 => 440,
151            GlslVersion::Glsl450 => 450,
152            GlslVersion::Glsl460 => 460,
153            GlslVersion::Glsl100Es => 100,
154            GlslVersion::Glsl300Es => 300,
155            GlslVersion::Glsl310Es => 310,
156            GlslVersion::Glsl320Es => 320,
157        };
158
159        let es = matches!(
160            self,
161            GlslVersion::Glsl100Es
162                | GlslVersion::Glsl300Es
163                | GlslVersion::Glsl310Es
164                | GlslVersion::Glsl320Es
165        );
166
167        unsafe {
168            sys::spvc_compiler_options_set_uint(
169                options,
170                spvc_compiler_option::SPVC_COMPILER_OPTION_GLSL_VERSION,
171                version,
172            )
173            .ok(root)?;
174            sys::spvc_compiler_options_set_bool(
175                options,
176                spvc_compiler_option::SPVC_COMPILER_OPTION_GLSL_ES,
177                es,
178            )
179            .ok(root)?;
180        }
181
182        Ok(())
183    }
184}
185
186impl Compiler<Glsl> {
187    /// Legacy GLSL compatibility method.
188    ///
189    /// Takes a uniform or push constant variable and flattens it into a `(i|u)vec4 array[N];` array instead.
190    /// For this to work, all types in the block must be the same basic type, e.g. mixing `vec2` and `vec4` is fine, but
191    /// mixing int and float is not.
192    ///
193    /// The name of the uniform array will be the same as the interface block name.
194    pub fn flatten_buffer_block(
195        &mut self,
196        block: impl Into<Handle<VariableId>>,
197    ) -> error::Result<()> {
198        let block = block.into();
199        let block = self.yield_id(block)?;
200
201        unsafe { sys::spvc_compiler_flatten_buffer_block(self.ptr.as_ptr(), block).ok(&*self) }
202    }
203
204    /// Returns the list of required extensions in a GLSL shader.
205    ///
206    /// If called after compilation this will contain any other extensions that the compiler
207    /// used automatically, in addition to the user specified ones.
208    pub fn required_extensions(&self) -> GlslExtensionsIter {
209        // SAFETY:
210        // It is **not sound** to return 'ctx here, the returned strings
211        // are from the compiler instance and can be mutated with require_extension
212        // https://github.com/KhronosGroup/SPIRV-Cross/blob/main/spirv_cross_c.cpp#L874
213        unsafe {
214            let extension_nums = sys::spvc_compiler_get_num_required_extensions(self.ptr.as_ptr());
215            let range = 0..extension_nums;
216            GlslExtensionsIter(range, self.phantom(), PhantomData)
217        }
218    }
219}
220
221/// Iterator for required GLSL extensions, created by [`Compiler<Glsl>::required_extensions`].
222pub struct GlslExtensionsIter<'a>(
223    // 'a is 'compiler.
224    Range<usize>,
225    // this is strictly speaking an abuse of PhantomCompiler,
226    // which should be invariant in 'ctx, but as long as its properly returned in
227    // required_extensions we should be safe.
228    PhantomCompiler,
229    PhantomData<&'a Glsl>,
230);
231
232impl_iterator!(GlslExtensionsIter<'c>: CompilerStr<'c> as and_then |s, index: usize| {
233    unsafe {
234        let extension = sys::spvc_compiler_get_required_extension(s.1.ptr.as_ptr(), index);
235        if extension.is_null() {
236            if cfg!(debug_assertions) {
237                panic!("Unexpected null string returned by `spvc_compiler_get_required_extension`.\
238                            The index of `spvc_compiler_get_num_required_extensions` did not match, complain to SPIRV-Cross.")
239            };
240            None
241        } else {
242            Some(CompilerStr::from_ptr(extension, s.1.ctx.clone()))
243        }
244    }
245} for <'c> [0]);
246
247#[cfg(test)]
248mod test {
249    use crate::compile::glsl::CompilerOptions;
250    use spirv_cross_sys::spvc_compiler_create_compiler_options;
251
252    use crate::compile::CompilableTarget;
253    use crate::error::{SpirvCrossError, ToContextError};
254    use crate::targets::Glsl;
255    use crate::Compiler;
256    use crate::{targets, Module};
257
258    static BASIC_SPV: &[u8] = include_bytes!("../../../basic.spv");
259
260    #[test]
261    pub fn glsl_opts() -> Result<(), SpirvCrossError> {
262        use crate::compile::sealed::ApplyCompilerOptions;
263
264        let words = Vec::from(BASIC_SPV);
265        let words = Module::from_words(bytemuck::cast_slice(&words));
266
267        let compiler: Compiler<targets::Glsl> = Compiler::new(words)?;
268        let resources = compiler.shader_resources()?.all_resources()?;
269
270        let mut opts_ptr = std::ptr::null_mut();
271
272        unsafe {
273            spvc_compiler_create_compiler_options(compiler.ptr.as_ptr(), &mut opts_ptr)
274                .ok(&compiler)?;
275        }
276
277        // println!("{:#?}", resources);
278        let opts = CompilerOptions::default();
279        unsafe {
280            opts.apply(opts_ptr, &compiler)?;
281        }
282
283        Ok(())
284    }
285
286    #[test]
287    pub fn required_extensions() -> Result<(), SpirvCrossError> {
288        let words = Vec::from(BASIC_SPV);
289        let words = Module::from_words(bytemuck::cast_slice(&words));
290
291        let mut compiler: Compiler<targets::Glsl> = Compiler::new(words)?;
292
293        compiler.require_extension("GL_KHR_my_Extension")?;
294        let extensions = compiler.required_extensions();
295        assert_eq!(
296            &["GL_KHR_my_Extension"],
297            extensions.collect::<Vec<_>>().as_slice()
298        );
299
300        compiler.require_extension("GL_KHR_my_ExtensionS")?;
301        compiler.require_extension("GL_KHR_my_ExtensionS")?;
302
303        let extensions: Vec<_> = compiler.required_extensions().collect();
304        assert_eq!(
305            &["GL_KHR_my_Extension", "GL_KHR_my_ExtensionS"],
306            extensions.as_slice()
307        );
308
309        let extensions = compiler.required_extensions();
310        let artifact = compiler.compile(&Glsl::options())?;
311        let extensions = artifact.required_extensions();
312
313        assert_eq!(
314            &["GL_KHR_my_Extension", "GL_KHR_my_ExtensionS"],
315            extensions.collect::<Vec<_>>().as_slice()
316        );
317
318        Ok(())
319    }
320}