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