Skip to main content

shaderc_dyn/
lib.rs

1// Copyright 2016 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Rust binding for the Shaderc library (dynamic loading fork).
16//!
17//! This crate contains the higher-level Rust-friendly interface for the
18//! Shaderc library. For the lower-level C interface, please see the
19//! [shaderc-sys-dyn](https://docs.rs/shaderc-sys-dyn) crate.
20//!
21//! The [Shaderc](https://github.com/google/shaderc) library provides an API
22//! for compiling GLSL/HLSL source code to SPIRV modules. It has been shipping
23//! in the Android NDK since version r12b.
24//!
25//! This fork loads the shaderc shared library (`libshaderc_shared`) at runtime
26//! using `libloading`. No build-time compilation is performed. The shared
27//! library must be installed and available on the system library search path,
28//! or its location can be specified via the `SHADERC_LIB_PATH` environment
29//! variable.
30//!
31//! # Examples
32//!
33//! Compile a shader into SPIR-V binary module and assembly text:
34//!
35//! ```
36//! use shaderc_dyn;
37//!
38//! let source = "#version 310 es\n void EP() {}";
39//!
40//! let compiler = shaderc_dyn::Compiler::new().unwrap();
41//! let mut options = shaderc_dyn::CompileOptions::new().unwrap();
42//! options.add_macro_definition("EP", Some("main"));
43//! let binary_result = compiler.compile_into_spirv(
44//!     source, shaderc_dyn::ShaderKind::Vertex,
45//!     "shader.glsl", "main", Some(&options)).unwrap();
46//!
47//! assert_eq!(Some(&0x07230203), binary_result.as_binary().first());
48//!
49//! let text_result = compiler.compile_into_spirv_assembly(
50//!     source, shaderc_dyn::ShaderKind::Vertex,
51//!     "shader.glsl", "main", Some(&options)).unwrap();
52//!
53//! assert!(text_result.as_text().starts_with("; SPIR-V\n"));
54//! ```
55
56#[cfg(test)]
57#[macro_use]
58extern crate assert_matches;
59extern crate libc;
60extern crate shaderc_sys_dyn;
61
62use shaderc_sys_dyn as scs;
63
64use libc::{c_char, c_int, c_void, size_t};
65use std::any::Any;
66use std::cell::RefCell;
67use std::ffi::{CStr, CString};
68use std::panic;
69use std::rc::Rc;
70use std::{error, fmt, ptr, result, slice, str};
71
72/// Error.
73///
74/// Each enumerant has an affixed string describing detailed reasons for
75/// the error. The string can be empty in certain cases.
76#[derive(Debug, PartialEq)]
77pub enum Error {
78    /// Compilation error.
79    ///
80    /// Contains the number of errors and a detailed error string.
81    CompilationError(u32, String),
82    InternalError(String),
83    InvalidStage(String),
84    InvalidAssembly(String),
85    NullResultObject(String),
86
87    /// Returned when an internal initialization fails, for instance creating
88    /// a `Compiler` or `CompileOptions` fails because the underlying shaderc
89    /// function returned null.
90    InitializationError(String),
91
92    /// Returned when string parsing fails (e.g., version/profile parsing).
93    ParseError(String),
94}
95
96impl fmt::Display for Error {
97    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98        match *self {
99            Error::CompilationError(count, ref reason) => {
100                if count == 1 {
101                    write!(f, "compilation error")?;
102                } else {
103                    write!(f, "{count} compilation errors")?;
104                }
105                if !reason.is_empty() {
106                    write!(
107                        f,
108                        ":{}{}",
109                        if reason.contains('\n') { "\n" } else { " " },
110                        reason
111                    )?;
112                }
113                Ok(())
114            }
115            Error::InternalError(ref r) => {
116                if r.is_empty() {
117                    write!(f, "internal error")
118                } else {
119                    write!(f, "internal error: {r}")
120                }
121            }
122            Error::InvalidStage(ref r) => {
123                if r.is_empty() {
124                    write!(f, "invalid stage")
125                } else {
126                    write!(f, "invalid stage: {r}")
127                }
128            }
129            Error::InvalidAssembly(ref r) => {
130                if r.is_empty() {
131                    write!(f, "invalid assembly")
132                } else {
133                    write!(f, "invalid assembly: {r}")
134                }
135            }
136            Error::NullResultObject(ref r) => {
137                if r.is_empty() {
138                    write!(f, "null result object")
139                } else {
140                    write!(f, "null result object: {r}")
141                }
142            }
143            Error::InitializationError(ref r) => {
144                if r.is_empty() {
145                    write!(f, "initialization error")
146                } else {
147                    write!(f, "initialization error: {r}")
148                }
149            }
150            Error::ParseError(ref r) => {
151                if r.is_empty() {
152                    write!(f, "parse error")
153                } else {
154                    write!(f, "parse error: {r}")
155                }
156            }
157        }
158    }
159}
160
161impl error::Error for Error {
162    fn description(&self) -> &str {
163        match *self {
164            Error::CompilationError(_, _) => "compilation error",
165            Error::InternalError(_) => "internal error",
166            Error::InvalidStage(_) => "invalid stage",
167            Error::InvalidAssembly(_) => "invalid assembly",
168            Error::NullResultObject(_) => "null result object",
169            Error::InitializationError(_) => "initialization error",
170            Error::ParseError(_) => "parse error",
171        }
172    }
173}
174
175/// Compilation status.
176pub type Result<T> = result::Result<T, Error>;
177
178/// Target environment.
179#[repr(C)]
180#[derive(Clone, Copy, Debug, PartialEq, Eq)]
181pub enum TargetEnv {
182    /// Compile under Vulkan semantics.
183    Vulkan,
184    /// Compile under OpenGL semantics.
185    OpenGL,
186    /// Compile under OpenGL semantics, including compatibility profile functions.
187    OpenGLCompat,
188}
189
190/// Target environment version.
191#[repr(C)]
192#[derive(Clone, Copy, Debug, PartialEq, Eq)]
193pub enum EnvVersion {
194    // For Vulkan, use Vulkan's mapping of version numbers to integers.
195    // See vulkan.h
196    Vulkan1_0 = 1 << 22,
197    Vulkan1_1 = (1 << 22) | (1 << 12),
198    Vulkan1_2 = (1 << 22) | (2 << 12),
199    Vulkan1_3 = (1 << 22) | (3 << 12),
200    Vulkan1_4 = (1 << 22) | (4 << 12),
201    // For OpenGL, use the number from #version in shaders.
202    // Currently no difference between OpenGL 4.5 and 4.6.
203    // See glslang/Standalone/Standalone.cpp
204    // Glslang doesn't accept a OpenGL client version of 460.
205    OpenGL4_5 = 450,
206    // Deprecated, WebGPU env never defined versions
207    WebGPU,
208}
209
210/// The known versions of SPIR-V.
211#[repr(C)]
212#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213pub enum SpirvVersion {
214    // Use the values used for word 1 of a SPIR-V binary:
215    // - bits 24 to 31: zero
216    // - bits 16 to 23: major version number
217    // - bits 8 to 15: minor version number
218    // - bits 0 to 7: zero
219    V1_0 = 0x0001_0000,
220    V1_1 = 0x0001_0100,
221    V1_2 = 0x0001_0200,
222    V1_3 = 0x0001_0300,
223    V1_4 = 0x0001_0400,
224    V1_5 = 0x0001_0500,
225    V1_6 = 0x0001_0600,
226}
227
228/// Source language.
229#[repr(C)]
230#[derive(Clone, Copy, Debug, PartialEq, Eq)]
231pub enum SourceLanguage {
232    GLSL,
233    HLSL,
234}
235
236/// Resource kinds.
237///
238/// In Vulkan, resources are bound to the pipeline via descriptors with
239/// numbered bindings and sets.
240#[repr(C)]
241#[derive(Clone, Copy, Debug, PartialEq, Eq)]
242pub enum ResourceKind {
243    /// Image and image buffer.
244    Image,
245    /// Pure sampler.
246    Sampler,
247    /// Sampled texture in GLSL, and Shader Resource View in HLSL.
248    Texture,
249    /// Uniform Buffer Object (UBO) in GLSL. cbuffer in HLSL.
250    Buffer,
251    /// Shader Storage Buffer Object (SSBO) in GLSL.
252    StorageBuffer,
253    /// Unordered Access View in HLSL. (Writable storage image or storage buffer.)
254    UnorderedAccessView,
255}
256
257/// Shader kind.
258///
259/// * The `<stage>` enumerants are forced shader kinds, which force the
260///   compiler to compile the source code as the specified kind of shader,
261///   regardless of `#pragma` directives in the source code.
262/// * The `Default<stage>` enumerants are default shader kinds, which
263///   allow the compiler to fall back to compile the source code as the
264///   specified kind of shader when `#pragma` is not found in the source
265///   code.
266#[repr(C)]
267#[derive(Clone, Copy, Debug, PartialEq, Eq)]
268pub enum ShaderKind {
269    Vertex,
270    Fragment,
271    Compute,
272    Geometry,
273    TessControl,
274    TessEvaluation,
275
276    /// Deduce the shader kind from `#pragma` directives in the source code.
277    ///
278    /// Compiler will emit error if `#pragma` annotation is not found.
279    InferFromSource,
280
281    DefaultVertex,
282    DefaultFragment,
283    DefaultCompute,
284    DefaultGeometry,
285    DefaultTessControl,
286    DefaultTessEvaluation,
287
288    SpirvAssembly,
289
290    RayGeneration,
291    AnyHit,
292    ClosestHit,
293    Miss,
294    Intersection,
295    Callable,
296
297    DefaultRayGeneration,
298    DefaultAnyHit,
299    DefaultClosestHit,
300    DefaultMiss,
301    DefaultIntersection,
302    DefaultCallable,
303
304    Task,
305    Mesh,
306
307    DefaultTask,
308    DefaultMesh,
309}
310
311/// GLSL profile.
312#[repr(C)]
313#[derive(Clone, Copy, Debug, PartialEq, Eq)]
314pub enum GlslProfile {
315    /// Used iff GLSL version did not specify the profile
316    None,
317    Core,
318    Compatibility,
319    Es,
320}
321
322/// Optimization level.
323#[repr(C)]
324#[derive(Clone, Copy, Debug, PartialEq, Eq)]
325pub enum OptimizationLevel {
326    /// No optimization
327    Zero,
328    /// Optimize towards reducing code size
329    Size,
330    Performance,
331}
332
333/// Resource limit.
334#[repr(C)]
335#[derive(Clone, Copy, Debug, PartialEq, Eq)]
336pub enum Limit {
337    MaxLights,
338    MaxClipPlanes,
339    MaxTextureUnits,
340    MaxTextureCoords,
341    MaxVertexAttribs,
342    MaxVertexUniformComponents,
343    MaxVaryingFloats,
344    MaxVertexTextureImageUnits,
345    MaxCombinedTextureImageUnits,
346    MaxTextureImageUnits,
347    MaxFragmentUniformComponents,
348    MaxDrawBuffers,
349    MaxVertexUniformVectors,
350    MaxVaryingVectors,
351    MaxFragmentUniformVectors,
352    MaxVertexOutputVectors,
353    MaxFragmentInputVectors,
354    MinProgramTexelOffset,
355    MaxProgramTexelOffset,
356    MaxClipDistances,
357    MaxComputeWorkGroupCountX,
358    MaxComputeWorkGroupCountY,
359    MaxComputeWorkGroupCountZ,
360    MaxComputeWorkGroupSizeX,
361    MaxComputeWorkGroupSizeY,
362    MaxComputeWorkGroupSizeZ,
363    MaxComputeUniformComponents,
364    MaxComputeTextureImageUnits,
365    MaxComputeImageUniforms,
366    MaxComputeAtomicCounters,
367    MaxComputeAtomicCounterBuffers,
368    MaxVaryingComponents,
369    MaxVertexOutputComponents,
370    MaxGeometryInputComponents,
371    MaxGeometryOutputComponents,
372    MaxFragmentInputComponents,
373    MaxImageUnits,
374    MaxCombinedImageUnitsAndFragmentOutputs,
375    MaxCombinedShaderOutputResources,
376    MaxImageSamples,
377    MaxVertexImageUniforms,
378    MaxTessControlImageUniforms,
379    MaxTessEvaluationImageUniforms,
380    MaxGeometryImageUniforms,
381    MaxFragmentImageUniforms,
382    MaxCombinedImageUniforms,
383    MaxGeometryTextureImageUnits,
384    MaxGeometryOutputVertices,
385    MaxGeometryTotalOutputComponents,
386    MaxGeometryUniformComponents,
387    MaxGeometryVaryingComponents,
388    MaxTessControlInputComponents,
389    MaxTessControlOutputComponents,
390    MaxTessControlTextureImageUnits,
391    MaxTessControlUniformComponents,
392    MaxTessControlTotalOutputComponents,
393    MaxTessEvaluationInputComponents,
394    MaxTessEvaluationOutputComponents,
395    MaxTessEvaluationTextureImageUnits,
396    MaxTessEvaluationUniformComponents,
397    MaxTessPatchComponents,
398    MaxPatchVertices,
399    MaxTessGenLevel,
400    MaxViewports,
401    MaxVertexAtomicCounters,
402    MaxTessControlAtomicCounters,
403    MaxTessEvaluationAtomicCounters,
404    MaxGeometryAtomicCounters,
405    MaxFragmentAtomicCounters,
406    MaxCombinedAtomicCounters,
407    MaxAtomicCounterBindings,
408    MaxVertexAtomicCounterBuffers,
409    MaxTessControlAtomicCounterBuffers,
410    MaxTessEvaluationAtomicCounterBuffers,
411    MaxGeometryAtomicCounterBuffers,
412    MaxFragmentAtomicCounterBuffers,
413    MaxCombinedAtomicCounterBuffers,
414    MaxAtomicCounterBufferSize,
415    MaxTransformFeedbackBuffers,
416    MaxTransformFeedbackInterleavedComponents,
417    MaxCullDistances,
418    MaxCombinedClipAndCullDistances,
419    MaxSamples,
420    MaxMeshOutputVerticesNv,
421    MaxMeshOutputPrimitivesNv,
422    MaxMeshWorkGroupSizeXNv,
423    MaxMeshWorkGroupSizeYNv,
424    MaxMeshWorkGroupSizeZNv,
425    MaxTaskWorkGroupSizeXNv,
426    MaxTaskWorkGroupSizeYNv,
427    MaxTaskWorkGroupSizeZNv,
428    MaxMeshViewCountNv,
429    MaxMeshOutputVerticesExt,
430    MaxMeshOutputPrimitivesExt,
431    MaxMeshWorkGroupSizeXExt,
432    MaxMeshWorkGroupSizeYExt,
433    MaxMeshWorkGroupSizeZExt,
434    MaxTaskWorkGroupSizeXExt,
435    MaxTaskWorkGroupSizeYExt,
436    MaxTaskWorkGroupSizeZExt,
437    MaxMeshViewCountExt,
438    MaxDualSourceDrawBuffersExt,
439}
440
441/// An opaque object managing all compiler states.
442///
443/// Creating an `Compiler` object has substantial resource costs; so it is
444/// recommended to keep one object around for all tasks.
445#[derive(Debug)]
446pub struct Compiler {
447    raw: *mut scs::ShadercCompiler,
448}
449
450unsafe impl Send for Compiler {}
451unsafe impl Sync for Compiler {}
452
453fn propagate_panic<F, T>(f: F) -> T
454where
455    F: FnOnce() -> T,
456{
457    PANIC_ERROR.with(|panic_error| {
458        *panic_error.borrow_mut() = None;
459    });
460    let result = f();
461    let err = PANIC_ERROR.with(|panic_error| panic_error.borrow_mut().take());
462    if let Some(err) = err {
463        panic::resume_unwind(err)
464    } else {
465        result
466    }
467}
468
469/// Returns a valid UTF-8 string from a slice of bytes.
470///
471/// A few shaderc functions have been observed to return invalid UTF-8 strings as
472/// warning/error messages, and instead of panicking and aborting execution this
473/// function can be used to convert the valid parts of the byte stream to
474/// a UTF-8 string
475fn safe_str_from_utf8(bytes: &[u8]) -> String {
476    match str::from_utf8(bytes) {
477        Ok(s) => s.to_string(),
478        Err(err) => {
479            if err.valid_up_to() > 0 {
480                format!(
481                    "{} (followed by invalid UTF-8 characters)",
482                    safe_str_from_utf8(&bytes[..err.valid_up_to()])
483                )
484            } else {
485                format!("invalid UTF-8 string: {err}")
486            }
487        }
488    }
489}
490
491impl Compiler {
492    /// Returns a `Compiler` object that can be used to compile SPIR-V modules.
493    ///
494    /// A return of `Err` indicates that there was an error initializing
495    /// the underlying compiler (i.e., `shaderc_compiler_initialize()` returned null).
496    pub fn new() -> Result<Self> {
497        let p = unsafe { scs::shaderc_compiler_initialize() };
498        if p.is_null() {
499            Err(Error::InitializationError(
500                "failed to create a shaderc compiler".to_string(),
501            ))
502        } else {
503            Ok(Compiler { raw: p })
504        }
505    }
506
507    fn handle_compilation_result(
508        result: *mut scs::ShadercCompilationResult,
509        is_binary: bool,
510    ) -> Result<CompilationArtifact> {
511        let status = unsafe { scs::shaderc_result_get_compilation_status(result) };
512        if status == 0 {
513            Ok(CompilationArtifact::new(result, is_binary))
514        } else {
515            let num_errors = unsafe { scs::shaderc_result_get_num_errors(result) } as u32;
516            let reason = unsafe {
517                let p = scs::shaderc_result_get_error_message(result);
518                let bytes = CStr::from_ptr(p).to_bytes();
519                safe_str_from_utf8(bytes)
520            };
521            match status {
522                1 => Err(Error::InvalidStage(reason)),
523                2 => Err(Error::CompilationError(num_errors, reason)),
524                3 => Err(Error::InternalError(reason)),
525                4 => Err(Error::NullResultObject(reason)),
526                5 => Err(Error::InvalidAssembly(reason)),
527                _ => panic!("unhandled shaderc error case"),
528            }
529        }
530    }
531
532    /// Compiles the given source string `source_text` to a SPIR-V binary
533    /// module according to the given `additional_options`.
534    ///
535    /// The source string will be compiled into a SPIR-V binary module
536    /// contained in a `CompilationArtifact` object if no error happens.
537    ///
538    /// The source string is treated as the given shader kind `shader_kind`.
539    /// If `InferFromSource` is given, the compiler will try to deduce the
540    /// shader kind from the source string via `#pragma` directives and a
541    /// failure in deducing will generate an error. If the shader kind is
542    /// set to one of the default shader kinds, the compiler will fall back
543    /// to the default shader kind in case it failed to deduce the shader
544    /// kind from the source string.
545    ///
546    /// `input_file_name` is a string used as a tag to identify the source
547    /// string in cases like emitting error messages. It doesn't have to be
548    /// a canonical "file name".
549    ///
550    /// `entry_point_name` is a string defines the name of the entry point
551    /// to associate with the source string.
552    pub fn compile_into_spirv(
553        &self,
554        source_text: &str,
555        shader_kind: ShaderKind,
556        input_file_name: &str,
557        entry_point_name: &str,
558        additional_options: Option<&CompileOptions>,
559    ) -> Result<CompilationArtifact> {
560        let source_size = source_text.len();
561        let c_source = CString::new(source_text).expect("cannot convert source_text to c string");
562        let c_file =
563            CString::new(input_file_name).expect("cannot convert input_file_name to c string");
564        let c_entry_point =
565            CString::new(entry_point_name).expect("cannot convert entry_point_name to c string");
566        propagate_panic(|| {
567            let result = unsafe {
568                scs::shaderc_compile_into_spv(
569                    self.raw,
570                    c_source.as_ptr(),
571                    source_size,
572                    shader_kind as i32,
573                    c_file.as_ptr(),
574                    c_entry_point.as_ptr(),
575                    additional_options.map_or(ptr::null(), |o| o.raw),
576                )
577            };
578            Compiler::handle_compilation_result(result, true)
579        })
580    }
581
582    /// Like `compile_into_spirv` but the result contains SPIR-V assembly text
583    /// instead of a SPIR-V binary module.
584    ///
585    /// The output SPIR-V assembly string will be of the format defined in
586    /// the [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools/blob/master/syntax.md)
587    /// project.
588    pub fn compile_into_spirv_assembly(
589        &self,
590        source_text: &str,
591        shader_kind: ShaderKind,
592        input_file_name: &str,
593        entry_point_name: &str,
594        additional_options: Option<&CompileOptions>,
595    ) -> Result<CompilationArtifact> {
596        let source_size = source_text.len();
597        let c_source = CString::new(source_text).expect("cannot convert source_text to c string");
598        let c_file =
599            CString::new(input_file_name).expect("cannot convert input_file_name to c string");
600        let c_entry_point =
601            CString::new(entry_point_name).expect("cannot convert entry_point_name to c string");
602        propagate_panic(|| {
603            let result = unsafe {
604                scs::shaderc_compile_into_spv_assembly(
605                    self.raw,
606                    c_source.as_ptr(),
607                    source_size,
608                    shader_kind as i32,
609                    c_file.as_ptr(),
610                    c_entry_point.as_ptr(),
611                    additional_options.map_or(ptr::null(), |o| o.raw),
612                )
613            };
614            Compiler::handle_compilation_result(result, false)
615        })
616    }
617
618    /// Like `compile_into_spirv` but the result contains preprocessed source
619    /// code instead of a SPIR-V binary module.
620    pub fn preprocess(
621        &self,
622        source_text: &str,
623        input_file_name: &str,
624        entry_point_name: &str,
625        additional_options: Option<&CompileOptions>,
626    ) -> Result<CompilationArtifact> {
627        let source_size = source_text.len();
628        let c_source = CString::new(source_text).expect("cannot convert source to c string");
629        let c_file =
630            CString::new(input_file_name).expect("cannot convert input_file_name to c string");
631        let c_entry_point =
632            CString::new(entry_point_name).expect("cannot convert entry_point_name to c string");
633        propagate_panic(|| {
634            let result = unsafe {
635                scs::shaderc_compile_into_preprocessed_text(
636                    self.raw,
637                    c_source.as_ptr(),
638                    source_size,
639                    // Stage doesn't matter for preprocess
640                    ShaderKind::Vertex as i32,
641                    c_file.as_ptr(),
642                    c_entry_point.as_ptr(),
643                    additional_options.map_or(ptr::null(), |o| o.raw),
644                )
645            };
646            Compiler::handle_compilation_result(result, false)
647        })
648    }
649
650    /// Assembles the given SPIR-V assembly string `source_assembly` into a
651    /// SPIR-V binary module according to the given `additional_options`.
652    ///
653    /// The input SPIR-V assembly string should be of the format defined in
654    /// the [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools/blob/master/syntax.md)
655    /// project.
656    ///
657    /// For options specified in `additional_options`, the assembling will
658    /// only pick those ones suitable for assembling.
659    pub fn assemble(
660        &self,
661        source_assembly: &str,
662        additional_options: Option<&CompileOptions>,
663    ) -> Result<CompilationArtifact> {
664        let source_size = source_assembly.len();
665        let c_source =
666            CString::new(source_assembly).expect("cannot convert source_assembly to c string");
667        propagate_panic(|| {
668            let result = unsafe {
669                scs::shaderc_assemble_into_spv(
670                    self.raw,
671                    c_source.as_ptr(),
672                    source_size,
673                    additional_options.map_or(ptr::null(), |o| o.raw),
674                )
675            };
676            Compiler::handle_compilation_result(result, true)
677        })
678    }
679}
680
681impl Drop for Compiler {
682    fn drop(&mut self) {
683        unsafe { scs::shaderc_compiler_release(self.raw) }
684    }
685}
686
687/// Include callback status.
688pub type IncludeCallbackResult = result::Result<ResolvedInclude, String>;
689
690type BoxedIncludeCallback<'a> =
691    Rc<dyn Fn(&str, IncludeType, &str, usize) -> IncludeCallbackResult + 'a>;
692
693/// An opaque object managing options to compilation.
694pub struct CompileOptions<'a> {
695    raw: *mut scs::ShadercCompileOptions,
696    include_callback_fn: Option<BoxedIncludeCallback<'a>>,
697}
698
699/// Identifies the type of include directive. `Relative` is for include directives of the form
700/// `#include "..."`, and `Standard` is for include directives of the form `#include <...>`.
701#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug)]
702pub enum IncludeType {
703    Relative,
704    Standard,
705}
706
707/// A representation of a successfully resolved include directive, containing the name of the include
708/// and its contents.
709#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)]
710pub struct ResolvedInclude {
711    /// A name uniquely identifying the resolved include. Typically the absolute path of the file.
712    ///
713    /// This name is used in error messages and to disambiguate different includes.
714    ///
715    /// This field must not be empty. Compilation will panic if an empty string is provided.
716    pub resolved_name: String,
717    /// The content of the include to substitute the include directive with.
718    pub content: String,
719}
720
721thread_local! {
722    static PANIC_ERROR: RefCell<Option<Box<dyn Any + Send + 'static>>> = RefCell::new(None);
723}
724
725impl<'a> CompileOptions<'a> {
726    /// Returns a default-initialized compilation options object.
727    ///
728    /// The default options are:
729    /// * Target environment: Vulkan
730    /// * Source language: GLSL
731    ///
732    /// Returns `Err(Error::InitializationError)` if creation failed.
733    pub fn new() -> Result<CompileOptions<'a>> {
734        let p = unsafe { scs::shaderc_compile_options_initialize() };
735        if p.is_null() {
736            Err(Error::InitializationError(
737                "failed to create CompileOptions".to_string(),
738            ))
739        } else {
740            Ok(CompileOptions {
741                raw: p,
742                include_callback_fn: None,
743            })
744        }
745    }
746
747    /// Sets the target environment to `env`, affecting which warnings or errors
748    /// will be issued.
749    ///
750    /// The default is Vulkan if not set.
751    ///
752    /// `version` will be used for distinguishing between different versions
753    /// of the target environment.
754    /// For example:
755    /// ```
756    /// let mut options = shaderc_dyn::CompileOptions::new().unwrap();
757    /// options.set_target_env(shaderc_dyn::TargetEnv::Vulkan, shaderc_dyn::EnvVersion::Vulkan1_1 as u32);
758    /// ```
759    pub fn set_target_env(&mut self, env: TargetEnv, version: u32) {
760        unsafe { scs::shaderc_compile_options_set_target_env(self.raw, env as i32, version) }
761    }
762
763    /// Sets the target SPIR-V version. The generated module will use this version
764    /// of SPIR-V. Each target environment determines what versions of SPIR-V
765    /// it can consume. Defaults to the highest version of SPIR-V 1.0 which is
766    /// required to be supported by the target environment.  E.g. Default to SPIR-V
767    /// 1.0 for Vulkan 1.0 and SPIR-V 1.3 for Vulkan 1.1.
768    pub fn set_target_spirv(&mut self, version: SpirvVersion) {
769        unsafe { scs::shaderc_compile_options_set_target_spirv(self.raw, version as i32) }
770    }
771
772    /// Sets the source language.
773    ///
774    /// The default is GLSL if not set.
775    pub fn set_source_language(&mut self, language: SourceLanguage) {
776        unsafe { scs::shaderc_compile_options_set_source_language(self.raw, language as i32) }
777    }
778
779    /// Forces the GLSL language `version` and `profile`.
780    ///
781    /// The version number is the same as would appear in the `#version`
782    /// directive in the source. Version and profile specified here
783    /// overrides the `#version` directive in the source code. Use
784    /// `GlslProfile::None` for GLSL versions that do not define profiles,
785    /// e.g., version below 150.
786    pub fn set_forced_version_profile(&mut self, version: u32, profile: GlslProfile) {
787        unsafe {
788            scs::shaderc_compile_options_set_forced_version_profile(
789                self.raw,
790                version as c_int,
791                profile as i32,
792            )
793        }
794    }
795
796    /// Sets the callback for handling the `#include` directive.
797    ///
798    /// The arguments to the callback are the name of the source being requested,
799    /// the type of include directive (`Relative` for `#include "foo"`, `Standard` for `#include <foo>`),
800    /// the name of the source containing the directive and the current include depth from the original
801    /// source.
802    ///
803    /// The return value of the callback should be `Ok` if the source was successfully found,
804    /// and an `Err` containing some suitable error message to display otherwise. If the result is
805    /// `Ok`, the `resolved_name` of the `ResolvedInclude` must be non-empty. All strings returned
806    /// from the callback must be convertible to `CString`s, i.e. they must not contain the null
807    /// character. If these conditions are not met compilation will panic.
808    ///
809    /// Behaviour note: If `Err` is returned for a `Relative` include request, the callback will be
810    /// tried again with `Standard`, which is similar to include directive behaviour in C.
811    pub fn set_include_callback<F>(&mut self, f: F)
812    where
813        F: Fn(&str, IncludeType, &str, usize) -> IncludeCallbackResult + 'a,
814    {
815        use std::mem;
816
817        let f = Rc::new(f);
818        let f_ptr = &*f as *const F;
819        self.include_callback_fn = Some(f as BoxedIncludeCallback<'a>);
820        unsafe {
821            scs::shaderc_compile_options_set_include_callbacks(
822                self.raw,
823                resolver::<'a, F>,
824                releaser,
825                f_ptr as *const c_void as *mut c_void,
826            );
827        }
828
829        struct OkResultWrapper {
830            source_name: CString,
831            content: CString,
832            wrapped: scs::shaderc_include_result,
833        }
834
835        struct ErrResultWrapper {
836            error_message: CString,
837            wrapped: scs::shaderc_include_result,
838        }
839
840        extern "C" fn resolver<'a, F>(
841            user_data: *mut c_void,
842            requested_source: *const c_char,
843            type_: c_int,
844            requesting_source: *const c_char,
845            include_depth: size_t,
846        ) -> *mut scs::shaderc_include_result
847        where
848            F: Fn(&str, IncludeType, &str, usize) -> IncludeCallbackResult + 'a,
849        {
850            let result = panic::catch_unwind(move || {
851                let f = unsafe { &*(user_data as *const F) };
852                let requested_source =
853                    unsafe { CStr::from_ptr(requested_source).to_string_lossy() };
854                let type_ = match type_ {
855                    0 => IncludeType::Relative,
856                    1 => IncludeType::Standard,
857                    x => panic!(
858                        "include callback: unknown include type returned from libshaderc: {}",
859                        x
860                    ),
861                };
862                let requesting_source =
863                    unsafe { CStr::from_ptr(requesting_source).to_string_lossy() };
864                match f(&requested_source, type_, &requesting_source, include_depth) {
865                    Ok(ResolvedInclude {
866                        resolved_name,
867                        content,
868                    }) => {
869                        if resolved_name.is_empty() {
870                            panic!("include callback: empty strings for resolved include names not allowed");
871                        }
872                        let mut result = Box::new(OkResultWrapper {
873                            source_name: CString::new(resolved_name).expect("include callback: could not convert resolved source name to a c string"),
874                            content: CString::new(content).expect("include callback: could not convert content string to a c string"),
875                            wrapped: unsafe { mem::zeroed() },
876                        });
877                        result.wrapped = scs::shaderc_include_result {
878                            source_name: result.source_name.as_ptr(),
879                            source_name_length: result.source_name.as_bytes().len(),
880                            content: result.content.as_ptr(),
881                            content_length: result.content.as_bytes().len(),
882                            user_data: &mut *result as *mut OkResultWrapper as *mut c_void,
883                        };
884                        let r = &mut result.wrapped as *mut scs::shaderc_include_result;
885                        mem::forget(result);
886                        r
887                    }
888                    Err(error_message) => {
889                        let mut result = Box::new(ErrResultWrapper {
890                            error_message: CString::new(error_message).expect(
891                                "include callback: could not convert error message to a c string",
892                            ),
893                            wrapped: unsafe { mem::zeroed() },
894                        });
895                        result.wrapped = scs::shaderc_include_result {
896                            source_name: CStr::from_bytes_with_nul(b"\0").unwrap().as_ptr(),
897                            source_name_length: 0,
898                            content: result.error_message.as_ptr(),
899                            content_length: result.error_message.as_bytes().len(),
900                            user_data: &mut *result as *mut ErrResultWrapper as *mut c_void,
901                        };
902                        let r = &mut result.wrapped as *mut scs::shaderc_include_result;
903                        mem::forget(result);
904                        r
905                    }
906                }
907            });
908            match result {
909                Ok(r) => r,
910                Err(e) => {
911                    PANIC_ERROR.with(|panic_error| {
912                        *panic_error.borrow_mut() = Some(e);
913                    });
914                    let mut result = Box::new(ErrResultWrapper {
915                        error_message: CString::new("").unwrap(),
916                        wrapped: unsafe { mem::zeroed() },
917                    });
918                    result.wrapped = scs::shaderc_include_result {
919                        source_name: CStr::from_bytes_with_nul(b"\0").unwrap().as_ptr(),
920                        source_name_length: 0,
921                        content: result.error_message.as_ptr(),
922                        content_length: 0,
923                        user_data: &mut *result as *mut ErrResultWrapper as *mut c_void,
924                    };
925                    let r = &mut result.wrapped as *mut scs::shaderc_include_result;
926                    mem::forget(result);
927                    r
928                }
929            }
930        }
931
932        extern "C" fn releaser(_: *mut c_void, include_result: *mut scs::shaderc_include_result) {
933            let user_data = unsafe { &*include_result }.user_data;
934            if unsafe { &*include_result }.source_name_length == 0 {
935                let wrapper = unsafe { Box::from_raw(user_data as *mut ErrResultWrapper) };
936                drop(wrapper);
937            } else {
938                let wrapper = unsafe { Box::from_raw(user_data as *mut OkResultWrapper) };
939                drop(wrapper);
940            }
941        }
942    }
943
944    /// Sets the resource `limit` to the given `value`.
945    pub fn set_limit(&mut self, limit: Limit, value: i32) {
946        unsafe { scs::shaderc_compile_options_set_limit(self.raw, limit as i32, value as c_int) }
947    }
948
949    /// Sets whether the compiler should automatically assign bindings to uniforms
950    /// that aren't already explicitly bound in the shader source.
951    pub fn set_auto_bind_uniforms(&mut self, auto_bind: bool) {
952        unsafe {
953            scs::shaderc_compile_options_set_auto_bind_uniforms(self.raw, auto_bind);
954        }
955    }
956
957    /// Sets whether the compiler should automatically remove sampler variables
958    /// and convert image variables to combined image-sampler variables.
959    pub fn set_auto_combined_image_sampler(&mut self, auto_combine: bool) {
960        unsafe {
961            scs::shaderc_compile_options_set_auto_combined_image_sampler(self.raw, auto_combine);
962        }
963    }
964
965    /// Sets whether the compiler should use HLSL IO mapping rules for bindings.
966    ///
967    /// Defaults to false.
968    pub fn set_hlsl_io_mapping(&mut self, hlsl_iomap: bool) {
969        unsafe {
970            scs::shaderc_compile_options_set_hlsl_io_mapping(self.raw, hlsl_iomap);
971        }
972    }
973
974    /// Sets whether the compiler should determine block member offsets using HLSL
975    /// packing rules instead of standard GLSL rules.
976    ///
977    /// Defaults to false. Only affects GLSL compilation. HLSL rules are always
978    /// used when compiling HLSL.
979    pub fn set_hlsl_offsets(&mut self, hlsl_offsets: bool) {
980        unsafe {
981            scs::shaderc_compile_options_set_hlsl_offsets(self.raw, hlsl_offsets);
982        }
983    }
984
985    /// Sets the base binding number used for for a resource type when automatically
986    /// assigning bindings.
987    ///
988    /// For GLSL compilation, sets the lowest automatically assigned number.
989    /// For HLSL compilation, the regsiter number assigned to the resource is added
990    /// to this specified base.
991    pub fn set_binding_base(&mut self, resource_kind: ResourceKind, base: u32) {
992        unsafe {
993            scs::shaderc_compile_options_set_binding_base(self.raw, resource_kind as i32, base);
994        }
995    }
996
997    /// Like `set_binding_base`, but only takes effect when compiling the given shader stage.
998    pub fn set_binding_base_for_stage(
999        &mut self,
1000        shader_kind: ShaderKind,
1001        resource_kind: ResourceKind,
1002        base: u32,
1003    ) {
1004        unsafe {
1005            scs::shaderc_compile_options_set_binding_base_for_stage(
1006                self.raw,
1007                shader_kind as i32,
1008                resource_kind as i32,
1009                base,
1010            );
1011        }
1012    }
1013
1014    /// Sets a descriptor set and binding for an HLSL register in all shader stages.
1015    pub fn set_hlsl_register_set_and_binding(&mut self, register: &str, set: &str, binding: &str) {
1016        let c_register = CString::new(register).expect("cannot convert register to c string");
1017        let c_set = CString::new(set).expect("cannot convert set to c string");
1018        let c_binding = CString::new(binding).expect("cannot convert binding to c string");
1019        unsafe {
1020            scs::shaderc_compile_options_set_hlsl_register_set_and_binding(
1021                self.raw,
1022                c_register.as_ptr(),
1023                c_set.as_ptr(),
1024                c_binding.as_ptr(),
1025            );
1026        }
1027    }
1028
1029    /// Automatically assigns locations to shader inputs and outputs.
1030    pub fn set_auto_map_locations(&mut self, auto_map: bool) {
1031        unsafe {
1032            scs::shaderc_compile_options_set_auto_map_locations(self.raw, auto_map);
1033        }
1034    }
1035
1036    /// Like `set_hlsl_register_set_and_binding`, but only takes effect when compiling
1037    /// the given shader stage.
1038    pub fn set_hlsl_register_set_and_binding_for_stage(
1039        &mut self,
1040        kind: ShaderKind,
1041        register: &str,
1042        set: &str,
1043        binding: &str,
1044    ) {
1045        let c_register = CString::new(register).expect("cannot convert string to c string");
1046        let c_set = CString::new(set).expect("cannot convert string to c string");
1047        let c_binding = CString::new(binding).expect("cannot convert string to c string");
1048        unsafe {
1049            scs::shaderc_compile_options_set_hlsl_register_set_and_binding_for_stage(
1050                self.raw,
1051                kind as i32,
1052                c_register.as_ptr(),
1053                c_set.as_ptr(),
1054                c_binding.as_ptr(),
1055            );
1056        }
1057    }
1058
1059    /// Sets whether the compiler should enable extension SPV_GOOGLE_hlsl_functionality1.
1060    pub fn set_hlsl_functionality1(&mut self, enable: bool) {
1061        unsafe {
1062            scs::shaderc_compile_options_set_hlsl_functionality1(self.raw, enable);
1063        }
1064    }
1065
1066    /// Sets whether the compiler should invert position.Y output in a vertex shader.
1067    pub fn set_invert_y(&mut self, enable: bool) {
1068        unsafe {
1069            scs::shaderc_compile_options_set_invert_y(self.raw, enable);
1070        }
1071    }
1072
1073    /// Sets whether the compiler generates code for max and min builtins which,
1074    /// if given a NaN operand, will return the other operand. Similarly, the clamp
1075    /// builtin will favour the non-NaN operands, as if clamp were implemented
1076    /// as a composition of max and min.
1077    pub fn set_nan_clamp(&mut self, enable: bool) {
1078        unsafe {
1079            scs::shaderc_compile_options_set_nan_clamp(self.raw, enable);
1080        }
1081    }
1082
1083    /// Adds a predefined macro to the compilation options.
1084    ///
1085    /// This has the same effect as passing `-Dname=value` to the command-line
1086    /// compiler.  If `value` is `None`, it has the same effect as passing
1087    /// `-Dname` to the command-line compiler. If a macro definition with the
1088    /// same name has previously been added, the value is replaced with the
1089    /// new value.
1090    pub fn add_macro_definition(&mut self, name: &str, value: Option<&str>) {
1091        let c_name = CString::new(name).expect("cannot convert macro name to c string");
1092        if let Some(value) = value {
1093            let c_value = CString::new(value).expect("cannot convert macro value to c string");
1094            unsafe {
1095                scs::shaderc_compile_options_add_macro_definition(
1096                    self.raw,
1097                    c_name.as_ptr(),
1098                    name.len(),
1099                    c_value.as_ptr(),
1100                    value.len(),
1101                )
1102            }
1103        } else {
1104            unsafe {
1105                scs::shaderc_compile_options_add_macro_definition(
1106                    self.raw,
1107                    c_name.as_ptr(),
1108                    name.len(),
1109                    ptr::null(),
1110                    0,
1111                )
1112            }
1113        }
1114    }
1115
1116    /// Sets the optimization level to `level`.
1117    ///
1118    /// If multiple invocations for this method, only the last one takes effect.
1119    pub fn set_optimization_level(&mut self, level: OptimizationLevel) {
1120        unsafe { scs::shaderc_compile_options_set_optimization_level(self.raw, level as i32) }
1121    }
1122
1123    /// Sets the compiler mode to generate debug information in the output.
1124    pub fn set_generate_debug_info(&mut self) {
1125        unsafe { scs::shaderc_compile_options_set_generate_debug_info(self.raw) }
1126    }
1127
1128    /// Sets the compiler mode to suppress warnings.
1129    ///
1130    /// This overrides warnings-as-errors mode: when both suppress-warnings and
1131    /// warnings-as-errors modes are turned on, warning messages will be
1132    /// inhibited, and will not be emitted as error messages.
1133    pub fn set_suppress_warnings(&mut self) {
1134        unsafe { scs::shaderc_compile_options_set_suppress_warnings(self.raw) }
1135    }
1136
1137    /// Sets the compiler mode to treat all warnings as errors.
1138    ///
1139    /// Note that the suppress-warnings mode overrides this.
1140    pub fn set_warnings_as_errors(&mut self) {
1141        unsafe { scs::shaderc_compile_options_set_warnings_as_errors(self.raw) }
1142    }
1143}
1144
1145impl Clone for CompileOptions<'_> {
1146    fn clone(&self) -> Self {
1147        let p = unsafe { scs::shaderc_compile_options_clone(self.raw) };
1148
1149        // This should never happen, outside of a heap allocation failure.
1150        // `shaderc_compile_options_clone()` is documented as being identical to
1151        // `shaderc_compile_options_init()` when passed a NULL ptr, and there
1152        // are no other failure conditions (it is a trivial C++ copy
1153        // constructor).
1154        assert!(
1155            !p.is_null(),
1156            "shaderc_compile_options_clone() returned NULL"
1157        );
1158
1159        CompileOptions {
1160            raw: p,
1161            include_callback_fn: self.include_callback_fn.clone(),
1162        }
1163    }
1164}
1165
1166impl Drop for CompileOptions<'_> {
1167    fn drop(&mut self) {
1168        unsafe { scs::shaderc_compile_options_release(self.raw) }
1169    }
1170}
1171
1172/// An opaque object containing the results of compilation.
1173pub struct CompilationArtifact {
1174    raw: *mut scs::ShadercCompilationResult,
1175    is_binary: bool,
1176}
1177
1178impl CompilationArtifact {
1179    fn new(result: *mut scs::ShadercCompilationResult, is_binary: bool) -> CompilationArtifact {
1180        CompilationArtifact {
1181            raw: result,
1182            is_binary,
1183        }
1184    }
1185
1186    /// Returns the number of bytes of the compilation output data.
1187    pub fn len(&self) -> usize {
1188        unsafe { scs::shaderc_result_get_length(self.raw) }
1189    }
1190
1191    /// Returns true if the compilation output data has a length of 0.
1192    pub fn is_empty(&self) -> bool {
1193        self.len() == 0
1194    }
1195
1196    /// Returns the compilation output data as a binary slice.
1197    ///
1198    /// # Panics
1199    ///
1200    /// Panics if the compilation does not generate a binary output.
1201    pub fn as_binary(&self) -> &[u32] {
1202        if !self.is_binary {
1203            panic!("not binary result")
1204        }
1205
1206        assert_eq!(0, self.len() % 4);
1207        let num_words = self.len() / 4;
1208
1209        unsafe {
1210            let p = scs::shaderc_result_get_bytes(self.raw);
1211            slice::from_raw_parts(p as *const u32, num_words)
1212        }
1213    }
1214
1215    /// Returns the compilation output data as a binary slice.
1216    /// This method return a &[u8] that implement the Read trait.
1217    ///
1218    /// # Panics
1219    ///
1220    /// Panics if the compilation does not generate a binary output.
1221    pub fn as_binary_u8(&self) -> &[u8] {
1222        if !self.is_binary {
1223            panic!("not binary result")
1224        }
1225
1226        assert_eq!(0, self.len() % 4);
1227
1228        unsafe {
1229            let p = scs::shaderc_result_get_bytes(self.raw);
1230            slice::from_raw_parts(p as *const u8, self.len())
1231        }
1232    }
1233
1234    /// Returns the compilation output data as a text string.
1235    ///
1236    /// # Panics
1237    ///
1238    /// Panics if the compilation does not generate a text output.
1239    pub fn as_text(&self) -> String {
1240        if self.is_binary {
1241            panic!("not text result")
1242        }
1243        unsafe {
1244            let p = scs::shaderc_result_get_bytes(self.raw);
1245            let bytes = CStr::from_ptr(p).to_bytes();
1246            str::from_utf8(bytes)
1247                .expect("invalid utf-8 string")
1248                .to_string()
1249        }
1250    }
1251
1252    /// Returns the number of warnings generated during the compilation.
1253    pub fn get_num_warnings(&self) -> u32 {
1254        unsafe { scs::shaderc_result_get_num_warnings(self.raw) as u32 }
1255    }
1256
1257    /// Returns the detailed warnings as a string.
1258    pub fn get_warning_messages(&self) -> String {
1259        unsafe {
1260            let p = scs::shaderc_result_get_error_message(self.raw);
1261            let bytes = CStr::from_ptr(p).to_bytes();
1262            safe_str_from_utf8(bytes)
1263        }
1264    }
1265}
1266
1267impl Drop for CompilationArtifact {
1268    fn drop(&mut self) {
1269        unsafe { scs::shaderc_result_release(self.raw) }
1270    }
1271}
1272
1273/// Returns the version and revision of the SPIR-V generated by this library.
1274///
1275/// The version number is a 32-bit word with the following four bytes
1276/// (high-order to low-order): `0 | Major Number | Minor Number | 0`.
1277/// So version 1.00 is of value 0x00010000.
1278pub fn get_spirv_version() -> (u32, u32) {
1279    let mut version: i32 = 0;
1280    let mut revision: i32 = 0;
1281    unsafe { scs::shaderc_get_spv_version(&mut version, &mut revision) };
1282    (version as u32, revision as u32)
1283}
1284
1285/// Parses the version and profile from the given `string`, returning a `Result`.
1286///
1287/// The string should contain both version and profile, like `450core`.
1288/// Returns an error if the string cannot be parsed.
1289pub fn parse_version_profile(string: &str) -> Result<(u32, GlslProfile)> {
1290    let mut version: i32 = 0;
1291    let mut profile: i32 = 0;
1292    let c_string = CString::new(string).expect("cannot convert string to c string");
1293    let result = unsafe {
1294        scs::shaderc_parse_version_profile(c_string.as_ptr(), &mut version, &mut profile)
1295    };
1296    if !result {
1297        Err(Error::ParseError(format!(
1298            "failed to parse version/profile from '{string}'"
1299        )))
1300    } else {
1301        let p = match profile {
1302            0 => GlslProfile::None,
1303            1 => GlslProfile::Core,
1304            2 => GlslProfile::Compatibility,
1305            3 => GlslProfile::Es,
1306            _ => panic!("internal error: unhandled profile value {}", profile),
1307        };
1308        Ok((version as u32, p))
1309    }
1310}
1311
1312#[cfg(test)]
1313mod tests {
1314    use super::*;
1315
1316    static VOID_MAIN: &str = "#version 310 es\n void main() {}";
1317    static VOID_E: &str = "#version 310 es\n void E() {}";
1318    static EXTRA_E: &str = "#version 310 es\n E\n void main() {}";
1319    static IFDEF_E: &str = "#version 310 es\n #ifdef E\n void main() {}\n\
1320                                    #else\n #error\n #endif";
1321    static HLSL_VERTEX: &str = "float4 main(uint index: SV_VERTEXID): SV_POSITION\n\
1322                                        { return float4(1., 2., 3., 4.); }";
1323    static TWO_ERROR: &str = "#version 310 es\n #error one\n #error two\n void main() {}";
1324    static TWO_ERROR_MSG: &str = "shader.glsl:2: error: '#error' : one\n\
1325                                          shader.glsl:3: error: '#error' : two\n";
1326    static ONE_WARNING: &str = "#version 400\n\
1327                                        layout(location = 0) attribute float x;\n void main() {}";
1328    static ONE_WARNING_MSG: &str = "\
1329shader.glsl:2: warning: attribute deprecated in version 130; may be removed in future release
1330";
1331    static DEBUG_INFO: &str = "#version 140\n \
1332                                       void main() {\n vec2 debug_info_sample = vec2(1.0);\n }";
1333    static CORE_PROFILE: &str = "void main() {\n gl_ClipDistance[0] = 5.;\n }";
1334
1335    static TWO_FN: &str = "\
1336#version 450
1337layout(location=0) in  int inVal;
1338layout(location=0) out int outVal;
1339int foo(int a) { return a; }
1340void main() { outVal = foo(inVal); }";
1341
1342    /// A shader that compiles under OpenGL compatibility but not core profile rules.
1343    static COMPAT_FRAG: &str = "\
1344#version 100
1345layout(binding = 0) uniform highp sampler2D tex;
1346void main() {
1347    gl_FragColor = texture2D(tex, vec2(0.));
1348}";
1349
1350    static VOID_MAIN_ASSEMBLY: &str = "\
1351; SPIR-V
1352; Version: 1.0
1353; Generator: Google Shaderc over Glslang; 11
1354; Bound: 6
1355; Schema: 0
1356               OpCapability Shader
1357          %1 = OpExtInstImport \"GLSL.std.450\"
1358               OpMemoryModel Logical GLSL450
1359               OpEntryPoint Vertex %main \"main\"
1360               OpSource ESSL 310
1361               OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"
1362               OpSourceExtension \"GL_GOOGLE_include_directive\"
1363               OpName %main \"main\"
1364       %void = OpTypeVoid
1365          %3 = OpTypeFunction %void
1366       %main = OpFunction %void None %3
1367          %5 = OpLabel
1368               OpReturn
1369               OpFunctionEnd
1370";
1371
1372    static UNIFORMS_NO_BINDINGS: &str = "\
1373#version 450
1374#extension GL_ARB_sparse_texture2 : enable
1375uniform texture2D my_tex;
1376uniform sampler my_sam;
1377layout(rgba32f) uniform image2D my_img;
1378layout(rgba32f) uniform imageBuffer my_imbuf;
1379uniform block { float x; float y; } my_ubo;
1380void main() {
1381  texture(sampler2D(my_tex,my_sam),vec2(1.0));
1382  vec4 t;
1383  sparseImageLoadARB(my_img,ivec2(0),t);
1384  imageLoad(my_imbuf,42);
1385  float x = my_ubo.x;
1386}";
1387
1388    static GLSL_EXPLICT_BINDING: &str = "\
1389#version 450
1390layout(set=0, binding=0)
1391buffer B { float x; vec3 y; } my_ssbo;
1392void main() { my_ssbo.x = 1.0; }";
1393
1394    #[test]
1395    fn test_compile_vertex_shader_into_spirv() {
1396        let c = Compiler::new().unwrap();
1397        let result = c
1398            .compile_into_spirv(VOID_MAIN, ShaderKind::Vertex, "shader.glsl", "main", None)
1399            .unwrap();
1400        assert!(result.len() > 20);
1401        assert!(result.as_binary().first() == Some(&0x0723_0203));
1402        let function_end_word: u32 = (1 << 16) | 56;
1403        assert!(result.as_binary().last() == Some(&function_end_word));
1404    }
1405
1406    #[test]
1407    fn test_compile_vertex_shader_into_spirv_assembly() {
1408        let c = Compiler::new().unwrap();
1409        let result = c
1410            .compile_into_spirv_assembly(VOID_MAIN, ShaderKind::Vertex, "shader.glsl", "main", None)
1411            .unwrap();
1412        assert_eq!(VOID_MAIN_ASSEMBLY, result.as_text());
1413    }
1414
1415    #[test]
1416    fn test_preprocess() {
1417        let c = Compiler::new().unwrap();
1418        let mut options = CompileOptions::new().unwrap();
1419        options.add_macro_definition("E", Some("main"));
1420        let result = c
1421            .preprocess(VOID_E, "shader.glsl", "main", Some(&options))
1422            .unwrap();
1423        assert_eq!("#version 310 es\n void main() { }\n", result.as_text());
1424    }
1425
1426    #[test]
1427    fn test_assemble() {
1428        let c = Compiler::new().unwrap();
1429        let result = c.assemble(VOID_MAIN_ASSEMBLY, None).unwrap();
1430        assert!(result.len() > 20);
1431        assert!(result.as_binary().first() == Some(&0x0723_0203));
1432        let function_end_word: u32 = (1 << 16) | 56;
1433        assert!(result.as_binary().last() == Some(&function_end_word));
1434    }
1435
1436    #[test]
1437    fn test_compile_options_add_macro_definition_normal_value() {
1438        let c = Compiler::new().unwrap();
1439        let mut options = CompileOptions::new().unwrap();
1440        options.add_macro_definition("E", Some("main"));
1441        let result = c
1442            .compile_into_spirv_assembly(
1443                VOID_E,
1444                ShaderKind::Vertex,
1445                "shader.glsl",
1446                "main",
1447                Some(&options),
1448            )
1449            .unwrap();
1450        assert_eq!(VOID_MAIN_ASSEMBLY, result.as_text());
1451    }
1452
1453    #[test]
1454    fn test_compile_options_add_macro_definition_empty_value() {
1455        let c = Compiler::new().unwrap();
1456        let mut options = CompileOptions::new().unwrap();
1457        options.add_macro_definition("E", Some(""));
1458        let result = c
1459            .compile_into_spirv_assembly(
1460                EXTRA_E,
1461                ShaderKind::Vertex,
1462                "shader.glsl",
1463                "main",
1464                Some(&options),
1465            )
1466            .unwrap();
1467        assert_eq!(VOID_MAIN_ASSEMBLY, result.as_text());
1468    }
1469
1470    #[test]
1471    fn test_compile_options_add_macro_definition_no_value() {
1472        let c = Compiler::new().unwrap();
1473        let mut options = CompileOptions::new().unwrap();
1474        options.add_macro_definition("E", None);
1475        let result = c
1476            .compile_into_spirv_assembly(
1477                IFDEF_E,
1478                ShaderKind::Vertex,
1479                "shader.glsl",
1480                "main",
1481                Some(&options),
1482            )
1483            .unwrap();
1484        assert_eq!(VOID_MAIN_ASSEMBLY, result.as_text());
1485    }
1486
1487    #[test]
1488    fn test_compile_options_clone() {
1489        let c = Compiler::new().unwrap();
1490        let mut options = CompileOptions::new().unwrap();
1491        options.add_macro_definition("E", None);
1492        let o = options.clone();
1493        let result = c
1494            .compile_into_spirv_assembly(
1495                IFDEF_E,
1496                ShaderKind::Vertex,
1497                "shader.glsl",
1498                "main",
1499                Some(&o),
1500            )
1501            .unwrap();
1502        assert_eq!(VOID_MAIN_ASSEMBLY, result.as_text());
1503    }
1504
1505    #[test]
1506    fn test_compile_options_set_source_language() {
1507        let c = Compiler::new().unwrap();
1508        let mut options = CompileOptions::new().unwrap();
1509        options.set_source_language(SourceLanguage::HLSL);
1510        let result = c
1511            .compile_into_spirv(
1512                HLSL_VERTEX,
1513                ShaderKind::Vertex,
1514                "shader.hlsl",
1515                "main",
1516                Some(&options),
1517            )
1518            .unwrap();
1519        assert!(result.len() > 20);
1520        assert!(result.as_binary().first() == Some(&0x0723_0203));
1521        let function_end_word: u32 = (1 << 16) | 56;
1522        assert!(result.as_binary().last() == Some(&function_end_word));
1523    }
1524
1525    #[test]
1526    fn test_compile_options_set_generate_debug_info() {
1527        let c = Compiler::new().unwrap();
1528        let mut options = CompileOptions::new().unwrap();
1529        options.set_generate_debug_info();
1530        let result = c
1531            .compile_into_spirv_assembly(
1532                DEBUG_INFO,
1533                ShaderKind::Vertex,
1534                "shader.glsl",
1535                "main",
1536                Some(&options),
1537            )
1538            .unwrap();
1539        assert!(result.as_text().contains("debug_info_sample"));
1540    }
1541
1542    #[test]
1543    fn test_compile_options_set_optimization_level_zero() {
1544        let c = Compiler::new().unwrap();
1545        let mut options = CompileOptions::new().unwrap();
1546        options.set_optimization_level(OptimizationLevel::Zero);
1547        let result = c
1548            .compile_into_spirv_assembly(
1549                DEBUG_INFO,
1550                ShaderKind::Vertex,
1551                "shader.glsl",
1552                "main",
1553                Some(&options),
1554            )
1555            .unwrap();
1556        assert!(result.as_text().contains("OpName"));
1557        assert!(result.as_text().contains("OpSource"));
1558    }
1559
1560    #[test]
1561    fn test_compile_options_set_optimization_level_size() {
1562        let c = Compiler::new().unwrap();
1563        let mut options = CompileOptions::new().unwrap();
1564        options.set_optimization_level(OptimizationLevel::Size);
1565        let result = c
1566            .compile_into_spirv_assembly(
1567                TWO_FN,
1568                ShaderKind::Vertex,
1569                "shader.glsl",
1570                "main",
1571                Some(&options),
1572            )
1573            .unwrap();
1574        assert!(!result.as_text().contains("OpFunctionCall"));
1575    }
1576
1577    #[test]
1578    fn test_compile_options_set_optimization_level_performance() {
1579        let c = Compiler::new().unwrap();
1580        let mut options = CompileOptions::new().unwrap();
1581        options.set_optimization_level(OptimizationLevel::Performance);
1582        let result = c
1583            .compile_into_spirv_assembly(
1584                TWO_FN,
1585                ShaderKind::Vertex,
1586                "shader.glsl",
1587                "main",
1588                Some(&options),
1589            )
1590            .unwrap();
1591        assert!(!result.as_text().contains("OpFunctionCall"));
1592    }
1593
1594    #[test]
1595    fn test_compile_options_set_forced_version_profile_ok() {
1596        let c = Compiler::new().unwrap();
1597        let mut options = CompileOptions::new().unwrap();
1598        options.set_forced_version_profile(450, GlslProfile::Core);
1599        let result = c
1600            .compile_into_spirv(
1601                CORE_PROFILE,
1602                ShaderKind::Vertex,
1603                "shader.glsl",
1604                "main",
1605                Some(&options),
1606            )
1607            .unwrap();
1608        assert!(result.len() > 20);
1609        assert!(result.as_binary().first() == Some(&0x0723_0203));
1610        let function_end_word: u32 = (1 << 16) | 56;
1611        assert!(result.as_binary().last() == Some(&function_end_word));
1612    }
1613
1614    #[test]
1615    fn test_compile_options_set_forced_version_profile_err() {
1616        let c = Compiler::new().unwrap();
1617        let mut options = CompileOptions::new().unwrap();
1618        options.set_forced_version_profile(310, GlslProfile::Es);
1619        let result = c.compile_into_spirv(
1620            CORE_PROFILE,
1621            ShaderKind::Vertex,
1622            "shader.glsl",
1623            "main",
1624            Some(&options),
1625        );
1626        assert!(result.is_err());
1627        assert_matches!(result.err(),
1628                        Some(Error::CompilationError(3, ref s))
1629                            if s.contains("error: 'gl_ClipDistance' : undeclared identifier"));
1630    }
1631
1632    #[test]
1633    #[should_panic(expected = "panic in include resolver!")]
1634    fn test_include_directive_panic() {
1635        let c = Compiler::new().unwrap();
1636        let mut options = CompileOptions::new().unwrap();
1637        options.set_include_callback(|_, _, _, _| panic!("panic in include resolver!"));
1638        drop(c.compile_into_spirv_assembly(
1639            r#"
1640            #version 400
1641            #include "foo.glsl"
1642            "#,
1643            ShaderKind::Vertex,
1644            "shader.glsl",
1645            "main",
1646            Some(&options),
1647        ));
1648    }
1649
1650    #[test]
1651    fn test_include_directive_err() {
1652        let c = Compiler::new().unwrap();
1653        let mut options = CompileOptions::new().unwrap();
1654        options
1655            .set_include_callback(|name, _, _, _| Err(format!("couldn't find header \"{name}\"")));
1656        let result = c.compile_into_spirv_assembly(
1657            r#"
1658            #version 400
1659            #include "foo.glsl"
1660            "#,
1661            ShaderKind::Vertex,
1662            "shader.glsl",
1663            "main",
1664            Some(&options),
1665        );
1666        assert!(result.is_err());
1667        assert_matches!(result.err(),
1668            Some(Error::CompilationError(1, ref s))
1669            if s.contains("couldn't find header \"foo.glsl\""));
1670    }
1671
1672    #[test]
1673    fn test_include_directive_success() {
1674        let c = Compiler::new().unwrap();
1675        let mut options = CompileOptions::new().unwrap();
1676        options.set_include_callback(|name, type_, _, _| {
1677            if name == "foo.glsl" && type_ == IncludeType::Relative {
1678                Ok(ResolvedInclude {
1679                    resolved_name: "std/foo.glsl".to_string(),
1680                    content: r#"
1681                    #ifndef FOO_H
1682                    #define FOO_H
1683                    void main() {}
1684                    #endif
1685                    "#
1686                    .to_string(),
1687                })
1688            } else {
1689                Err(format!("couldn't find header \"{name}\""))
1690            }
1691        });
1692        let result = c.compile_into_spirv_assembly(
1693            r#"
1694            #version 400
1695            #include "foo.glsl"
1696            #include "foo.glsl"
1697            "#,
1698            ShaderKind::Vertex,
1699            "shader.glsl",
1700            "main",
1701            Some(&options),
1702        );
1703        assert_matches!(result.err(), None);
1704    }
1705
1706    #[test]
1707    fn test_compile_options_set_suppress_warnings() {
1708        let c = Compiler::new().unwrap();
1709        let mut options = CompileOptions::new().unwrap();
1710        options.set_suppress_warnings();
1711        let result = c
1712            .compile_into_spirv(
1713                ONE_WARNING,
1714                ShaderKind::Vertex,
1715                "shader.glsl",
1716                "main",
1717                Some(&options),
1718            )
1719            .unwrap();
1720        assert_eq!(0, result.get_num_warnings());
1721    }
1722
1723    #[test]
1724    fn test_compile_options_set_warnings_as_errors() {
1725        let c = Compiler::new().unwrap();
1726        let mut options = CompileOptions::new().unwrap();
1727        options.set_warnings_as_errors();
1728        let result = c.compile_into_spirv(
1729            ONE_WARNING,
1730            ShaderKind::Vertex,
1731            "shader.glsl",
1732            "main",
1733            Some(&options),
1734        );
1735        assert!(result.is_err());
1736        assert_matches!(result.err(),
1737                        Some(Error::CompilationError(1, ref s))
1738                            if s.contains("error: attribute deprecated in version 130;"));
1739    }
1740
1741    #[test]
1742    fn test_compile_options_set_target_env_err_vulkan() {
1743        let c = Compiler::new().unwrap();
1744        let result = c.compile_into_spirv(
1745            COMPAT_FRAG,
1746            ShaderKind::Fragment,
1747            "shader.glsl",
1748            "main",
1749            None,
1750        );
1751        assert!(result.is_err());
1752        assert_matches!(result.err(),
1753                        Some(Error::CompilationError(4, ref s))
1754                            if s.contains("error: #version: ES shaders for SPIR-V \
1755                                           require version 310 or higher"));
1756    }
1757
1758    #[test]
1759    fn test_compile_options_set_target_env_err_opengl() {
1760        let c = Compiler::new().unwrap();
1761        let mut options = CompileOptions::new().unwrap();
1762        options.set_target_env(TargetEnv::OpenGL, 0);
1763        let result = c.compile_into_spirv(
1764            COMPAT_FRAG,
1765            ShaderKind::Fragment,
1766            "shader.glsl",
1767            "main",
1768            Some(&options),
1769        );
1770        assert!(result.is_err());
1771        assert_matches!(result.err(),
1772                        Some(Error::CompilationError(3, ref s))
1773                            if s.contains("error: #version: ES shaders for SPIR-V require \
1774                                           version 310 or higher"));
1775    }
1776
1777    /// Returns a fragment shader accessing a texture with the given offset.
1778    macro_rules! texture_offset {
1779        ($offset:expr) => {{
1780            let mut s = "#version 450
1781                         layout (binding=0) uniform sampler1D tex;
1782                         void main() {
1783                            vec4 x = textureOffset(tex, 1., "
1784                .to_string();
1785            s.push_str(stringify!($offset));
1786            s.push_str(");\n}");
1787            s
1788        }};
1789    }
1790
1791    #[test]
1792    fn test_compile_options_set_limit() {
1793        let c = Compiler::new().unwrap();
1794        let mut options = CompileOptions::new().unwrap();
1795        assert!(c
1796            .compile_into_spirv(
1797                &texture_offset!(7),
1798                ShaderKind::Fragment,
1799                "shader.glsl",
1800                "main",
1801                Some(&options)
1802            )
1803            .is_ok());
1804        assert!(c
1805            .compile_into_spirv(
1806                &texture_offset!(8),
1807                ShaderKind::Fragment,
1808                "shader.glsl",
1809                "main",
1810                Some(&options)
1811            )
1812            .is_err());
1813        options.set_limit(Limit::MaxProgramTexelOffset, 10);
1814        assert!(c
1815            .compile_into_spirv(
1816                &texture_offset!(8),
1817                ShaderKind::Fragment,
1818                "shader.glsl",
1819                "main",
1820                Some(&options)
1821            )
1822            .is_ok());
1823        assert!(c
1824            .compile_into_spirv(
1825                &texture_offset!(10),
1826                ShaderKind::Fragment,
1827                "shader.glsl",
1828                "main",
1829                Some(&options)
1830            )
1831            .is_ok());
1832        assert!(c
1833            .compile_into_spirv(
1834                &texture_offset!(11),
1835                ShaderKind::Fragment,
1836                "shader.glsl",
1837                "main",
1838                Some(&options)
1839            )
1840            .is_err());
1841    }
1842
1843    #[test]
1844    fn test_compile_options_set_auto_bind_uniforms_false() {
1845        let c = Compiler::new().unwrap();
1846        let mut options = CompileOptions::new().unwrap();
1847        options.set_auto_bind_uniforms(false);
1848        let result = c.compile_into_spirv_assembly(
1849            UNIFORMS_NO_BINDINGS,
1850            ShaderKind::Vertex,
1851            "shader.glsl",
1852            "main",
1853            Some(&options),
1854        );
1855        assert!(result.is_err());
1856        assert_matches!(result.err(),
1857                        Some(Error::CompilationError(_, ref s))
1858                            if s.contains("error: 'binding' : sampler/texture/image requires layout(binding=X)"));
1859    }
1860
1861    #[test]
1862    fn test_compile_options_set_auto_bind_uniforms_true() {
1863        let c = Compiler::new().unwrap();
1864        let mut options = CompileOptions::new().unwrap();
1865        options.set_auto_bind_uniforms(true);
1866        let result = c
1867            .compile_into_spirv_assembly(
1868                UNIFORMS_NO_BINDINGS,
1869                ShaderKind::Vertex,
1870                "shader.glsl",
1871                "main",
1872                Some(&options),
1873            )
1874            .unwrap()
1875            .as_text();
1876        assert!(result.contains("OpDecorate %my_tex Binding 0"));
1877        assert!(result.contains("OpDecorate %my_sam Binding 1"));
1878        assert!(result.contains("OpDecorate %my_img Binding 2"));
1879        assert!(result.contains("OpDecorate %my_imbuf Binding 3"));
1880        assert!(result.contains("OpDecorate %my_ubo Binding 4"));
1881    }
1882
1883    #[test]
1884    fn test_compile_options_set_hlsl_offsets_false() {
1885        let c = Compiler::new().unwrap();
1886        let mut options = CompileOptions::new().unwrap();
1887        options.set_hlsl_offsets(false);
1888        let result = c
1889            .compile_into_spirv_assembly(
1890                GLSL_EXPLICT_BINDING,
1891                ShaderKind::Vertex,
1892                "shader.glsl",
1893                "main",
1894                Some(&options),
1895            )
1896            .unwrap()
1897            .as_text();
1898        assert!(result.contains("OpMemberDecorate %B 1 Offset 16"));
1899    }
1900
1901    #[test]
1902    fn test_compile_options_set_hlsl_offsets_true() {
1903        let c = Compiler::new().unwrap();
1904        let mut options = CompileOptions::new().unwrap();
1905        options.set_hlsl_offsets(true);
1906        let result = c
1907            .compile_into_spirv_assembly(
1908                GLSL_EXPLICT_BINDING,
1909                ShaderKind::Vertex,
1910                "shader.glsl",
1911                "main",
1912                Some(&options),
1913            )
1914            .unwrap()
1915            .as_text();
1916        assert!(result.contains("OpMemberDecorate %B 1 Offset 4"));
1917    }
1918
1919    #[test]
1920    fn test_compile_options_set_binding_base() {
1921        let c = Compiler::new().unwrap();
1922        let mut options = CompileOptions::new().unwrap();
1923        options.set_auto_bind_uniforms(true);
1924        options.set_binding_base(ResourceKind::Image, 44);
1925        let result = c
1926            .compile_into_spirv_assembly(
1927                UNIFORMS_NO_BINDINGS,
1928                ShaderKind::Vertex,
1929                "shader.glsl",
1930                "main",
1931                Some(&options),
1932            )
1933            .unwrap()
1934            .as_text();
1935        assert!(result.contains("OpDecorate %my_tex Binding 0"));
1936        assert!(result.contains("OpDecorate %my_sam Binding 1"));
1937        assert!(result.contains("OpDecorate %my_img Binding 44"));
1938        assert!(result.contains("OpDecorate %my_imbuf Binding 45"));
1939        assert!(result.contains("OpDecorate %my_ubo Binding 2"));
1940    }
1941
1942    #[test]
1943    fn test_compile_options_set_binding_base_for_stage_effective() {
1944        let c = Compiler::new().unwrap();
1945        let mut options = CompileOptions::new().unwrap();
1946        options.set_auto_bind_uniforms(true);
1947        options.set_binding_base_for_stage(ShaderKind::Vertex, ResourceKind::Texture, 100);
1948        let result = c
1949            .compile_into_spirv_assembly(
1950                UNIFORMS_NO_BINDINGS,
1951                ShaderKind::Vertex,
1952                "shader.glsl",
1953                "main",
1954                Some(&options),
1955            )
1956            .unwrap()
1957            .as_text();
1958        assert!(result.contains("OpDecorate %my_tex Binding 100"));
1959        assert!(result.contains("OpDecorate %my_sam Binding 0"));
1960        assert!(result.contains("OpDecorate %my_img Binding 1"));
1961        assert!(result.contains("OpDecorate %my_imbuf Binding 2"));
1962        assert!(result.contains("OpDecorate %my_ubo Binding 3"));
1963    }
1964
1965    #[test]
1966    fn test_compile_options_set_binding_base_for_stage_ignore() {
1967        let c = Compiler::new().unwrap();
1968        let mut options = CompileOptions::new().unwrap();
1969        options.set_auto_bind_uniforms(true);
1970        options.set_binding_base_for_stage(ShaderKind::Fragment, ResourceKind::Texture, 100);
1971        let result = c
1972            .compile_into_spirv_assembly(
1973                UNIFORMS_NO_BINDINGS,
1974                ShaderKind::Vertex,
1975                "shader.glsl",
1976                "main",
1977                Some(&options),
1978            )
1979            .unwrap()
1980            .as_text();
1981        assert!(result.contains("OpDecorate %my_tex Binding 0"));
1982        assert!(result.contains("OpDecorate %my_sam Binding 1"));
1983        assert!(result.contains("OpDecorate %my_img Binding 2"));
1984        assert!(result.contains("OpDecorate %my_imbuf Binding 3"));
1985        assert!(result.contains("OpDecorate %my_ubo Binding 4"));
1986    }
1987
1988    #[test]
1989    fn test_error_compilation_error() {
1990        let c = Compiler::new().unwrap();
1991        let result =
1992            c.compile_into_spirv(TWO_ERROR, ShaderKind::Vertex, "shader.glsl", "main", None);
1993        assert!(result.is_err());
1994        assert_eq!(
1995            Some(Error::CompilationError(2, TWO_ERROR_MSG.to_string())),
1996            result.err()
1997        );
1998    }
1999
2000    #[test]
2001    fn test_error_invalid_stage() {
2002        let c = Compiler::new().unwrap();
2003        let result = c.compile_into_spirv(
2004            VOID_MAIN,
2005            ShaderKind::InferFromSource,
2006            "shader.glsl",
2007            "main",
2008            None,
2009        );
2010        assert!(result.is_err());
2011        assert_eq!(Some(Error::InvalidStage("".to_string())), result.err());
2012    }
2013
2014    #[test]
2015    fn test_warning() {
2016        let c = Compiler::new().unwrap();
2017        let result = c
2018            .compile_into_spirv(ONE_WARNING, ShaderKind::Vertex, "shader.glsl", "main", None)
2019            .unwrap();
2020        assert_eq!(1, result.get_num_warnings());
2021        assert_eq!(ONE_WARNING_MSG.to_string(), result.get_warning_messages());
2022    }
2023
2024    #[test]
2025    fn test_get_spirv_version() {
2026        let (version, _) = get_spirv_version();
2027        assert_eq!((1 << 16) + (6 << 8), version);
2028    }
2029
2030    #[test]
2031    fn test_parse_version_profile() {
2032        assert_eq!(Ok((310, GlslProfile::Es)), parse_version_profile("310es"));
2033        assert_eq!(
2034            Ok((450, GlslProfile::Compatibility)),
2035            parse_version_profile("450compatibility")
2036        );
2037        assert_eq!(Ok((140, GlslProfile::None)), parse_version_profile("140"));
2038        assert_eq!(
2039            Err(Error::ParseError(
2040                "failed to parse version/profile from 'something'".to_string()
2041            )),
2042            parse_version_profile("something")
2043        );
2044        assert_eq!(
2045            Err(Error::ParseError(
2046                "failed to parse version/profile from ''".to_string()
2047            )),
2048            parse_version_profile("")
2049        );
2050    }
2051}