Skip to main content

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