mozangle/shaders/
mod.rs

1#[allow(dead_code)]
2#[allow(non_upper_case_globals)]
3#[allow(non_camel_case_types)]
4#[allow(non_snake_case)]
5pub mod ffi {
6    include!(concat!(env!("OUT_DIR"), "/glslang_glue_bindings.rs"));
7}
8
9use self::ffi::ShShaderOutput::*;
10use self::ffi::ShShaderSpec::*;
11use self::ffi::*;
12
13use std::collections::HashMap;
14use std::default;
15use std::ffi::CStr;
16use std::ffi::CString;
17use std::mem::MaybeUninit;
18use std::os::raw::c_char;
19use std::os::raw::c_void;
20use std::slice;
21use std::str;
22use std::sync::Mutex;
23
24static CONSTRUCT_COMPILER_LOCK: Mutex<()> = Mutex::new(());
25
26pub fn initialize() -> Result<(), &'static str> {
27    if unsafe { GLSLangInitialize() } == 0 {
28        Err("Couldn't initialize GLSLang")
29    } else {
30        Ok(())
31    }
32}
33
34pub fn finalize() -> Result<(), &'static str> {
35    if unsafe { GLSLangFinalize() } == 0 {
36        Err("Couldn't finalize GLSLang")
37    } else {
38        Ok(())
39    }
40}
41
42pub trait AsAngleEnum {
43    fn as_angle_enum(&self) -> u32;
44}
45
46pub enum ShaderSpec {
47    Gles2,
48    WebGL,
49    Gles3,
50    WebGL2,
51    WebGL3,
52}
53
54impl AsAngleEnum for ShaderSpec {
55    #[inline]
56    fn as_angle_enum(&self) -> u32 {
57        (match *self {
58            ShaderSpec::Gles2 => SH_GLES2_SPEC,
59            ShaderSpec::WebGL => SH_WEBGL_SPEC,
60            ShaderSpec::Gles3 => SH_GLES3_SPEC,
61            ShaderSpec::WebGL2 => SH_WEBGL2_SPEC,
62            ShaderSpec::WebGL3 => SH_WEBGL3_SPEC,
63        }) as u32
64    }
65}
66
67pub enum Output {
68    Essl,
69    Glsl,
70    GlslCompat,
71    GlslCore,
72    Glsl130,
73    Glsl140,
74    Glsl150Core,
75    Glsl330Core,
76    Glsl400Core,
77    Glsl410Core,
78    Glsl420Core,
79    Glsl430Core,
80    Glsl440Core,
81    Glsl450Core,
82}
83
84impl AsAngleEnum for Output {
85    #[inline]
86    fn as_angle_enum(&self) -> u32 {
87        (match *self {
88            Output::Essl => SH_ESSL_OUTPUT,
89            Output::Glsl => SH_GLSL_COMPATIBILITY_OUTPUT,
90            Output::GlslCompat => SH_GLSL_COMPATIBILITY_OUTPUT,
91            Output::GlslCore => SH_GLSL_130_OUTPUT,
92            Output::Glsl130 => SH_GLSL_130_OUTPUT,
93            Output::Glsl140 => SH_GLSL_140_OUTPUT,
94            Output::Glsl150Core => SH_GLSL_150_CORE_OUTPUT,
95            Output::Glsl330Core => SH_GLSL_330_CORE_OUTPUT,
96            Output::Glsl400Core => SH_GLSL_400_CORE_OUTPUT,
97            Output::Glsl410Core => SH_GLSL_410_CORE_OUTPUT,
98            Output::Glsl420Core => SH_GLSL_420_CORE_OUTPUT,
99            Output::Glsl430Core => SH_GLSL_430_CORE_OUTPUT,
100            Output::Glsl440Core => SH_GLSL_440_CORE_OUTPUT,
101            Output::Glsl450Core => SH_GLSL_450_CORE_OUTPUT,
102        }) as u32
103    }
104}
105
106pub type BuiltInResources = ShBuiltInResources;
107
108impl default::Default for BuiltInResources {
109    fn default() -> BuiltInResources {
110        unsafe {
111            let mut ret: BuiltInResources = Self::empty();
112            GLSLangInitBuiltInResources(&mut ret);
113            ret
114        }
115    }
116}
117
118impl BuiltInResources {
119    #[inline]
120    pub fn empty() -> BuiltInResources {
121        unsafe { MaybeUninit::zeroed().assume_init() }
122    }
123}
124
125pub type CompileOptions = ShCompileOptions;
126
127impl CompileOptions {
128    pub fn mozangle() -> Self {
129        let mut options = unsafe { CompileOptions::new() };
130        options.set_objectCode(1);
131        options.set_variables(1); // For uniform_name_map()
132        options.set_emulateAbsIntFunction(1); // To workaround drivers
133        options.set_emulateIsnanFloatFunction(1); // To workaround drivers
134        options.set_emulateAtan2FloatFunction(1); // To workaround drivers
135        options.set_clampIndirectArrayBounds(1);
136        options.set_initGLPosition(1);
137        options.set_enforcePackingRestrictions(1);
138        options.set_limitExpressionComplexity(1);
139        options.set_limitCallStackDepth(1);
140        // Todo(Mortimer): Add SH_TIMING_RESTRICTIONS to options when the implementations gets better
141        // Right now SH_TIMING_RESTRICTIONS is experimental
142        // and doesn't support user callable functions in shaders
143        options
144    }
145}
146
147pub struct ShaderValidator {
148    handle: ShHandle,
149}
150
151impl ShaderValidator {
152    /// Create a new ShaderValidator instance
153    /// NB: To call this you should have called first
154    /// initialize()
155    pub fn new(
156        shader_type: u32,
157        spec: ShaderSpec,
158        output: Output,
159        resources: &BuiltInResources,
160    ) -> Option<ShaderValidator> {
161        // GLSLangConstructCompiler is non-thread safe because it internally calls TCache::getType()
162        // which writes/reads a std::map<T> with no locks.
163        let _guard = CONSTRUCT_COMPILER_LOCK.lock().unwrap();
164        let handle = unsafe {
165            GLSLangConstructCompiler(
166                shader_type,
167                spec.as_angle_enum(),
168                output.as_angle_enum(),
169                resources,
170            )
171        };
172
173        if handle.is_null() {
174            return None;
175        }
176
177        Some(ShaderValidator { handle: handle })
178    }
179
180    #[inline]
181    pub fn for_webgl(
182        shader_type: u32,
183        output: Output,
184        resources: &BuiltInResources,
185    ) -> Option<ShaderValidator> {
186        Self::new(shader_type, ShaderSpec::WebGL, output, resources)
187    }
188
189    #[inline]
190    pub fn for_webgl2(
191        shader_type: u32,
192        output: Output,
193        resources: &BuiltInResources,
194    ) -> Option<ShaderValidator> {
195        Self::new(shader_type, ShaderSpec::WebGL2, output, resources)
196    }
197
198    pub fn compile(&self, strings: &[&str], options: ShCompileOptions) -> Result<(), &'static str> {
199        let mut cstrings = Vec::with_capacity(strings.len());
200
201        for s in strings.iter() {
202            cstrings.push(CString::new(*s).map_err(|_| "Found invalid characters")?)
203        }
204
205        let cptrs: Vec<_> = cstrings.iter().map(|s| s.as_ptr()).collect();
206
207        if unsafe {
208            GLSLangCompile(
209                self.handle,
210                cptrs.as_ptr() as *const *const c_char,
211                cstrings.len(),
212                &options as *const _,
213            )
214        } == 0
215        {
216            return Err("Couldn't compile shader");
217        }
218        Ok(())
219    }
220
221    pub fn object_code(&self) -> String {
222        unsafe {
223            let c_str = CStr::from_ptr(GLSLangGetObjectCode(self.handle));
224            c_str.to_string_lossy().into_owned()
225        }
226    }
227
228    pub fn info_log(&self) -> String {
229        unsafe {
230            let c_str = CStr::from_ptr(GLSLangGetInfoLog(self.handle));
231            c_str.to_string_lossy().into_owned()
232        }
233    }
234
235    pub fn compile_and_translate(&self, strings: &[&str]) -> Result<String, &'static str> {
236        let options = CompileOptions::mozangle();
237        self.compile(strings, options)?;
238        Ok(self.object_code())
239    }
240
241    /// Returns a map from uniform name in the original shader to uniform name
242    /// in the compiled shader.
243    ///
244    /// The map can be empty if the `SH_VARIABLES` option wasn't specified.
245    pub fn uniform_name_map(&self) -> HashMap<String, String> {
246        struct Closure {
247            map: HashMap<String, String>,
248            error: Option<str::Utf8Error>,
249        }
250
251        unsafe extern "C" fn each_c(
252            closure: *mut c_void,
253            first: *const c_char,
254            first_len: usize,
255            second: *const c_char,
256            second_len: usize,
257        ) {
258            // Safety: code in or called from this function must not panic.
259            // If it might and https://github.com/rust-lang/rust/issues/18510 is not fixed yet,
260            // use std::panic::catch_unwind.
261            let closure = closure as *mut Closure;
262            let closure = &mut *closure;
263            if closure.error.is_none() {
264                macro_rules! to_string {
265                    ($ptr: expr, $len: expr) => {
266                        match str::from_utf8(slice::from_raw_parts($ptr as *const u8, $len)) {
267                            Ok(s) => s.to_owned(),
268                            Err(e) => {
269                                closure.error = Some(e);
270                                return;
271                            }
272                        }
273                    };
274                }
275                closure
276                    .map
277                    .insert(to_string!(first, first_len), to_string!(second, second_len));
278            }
279        }
280
281        let mut closure = Closure {
282            map: HashMap::new(),
283            error: None,
284        };
285        let closure_ptr: *mut Closure = &mut closure;
286        unsafe {
287            GLSLangIterUniformNameMapping(self.handle, Some(each_c), closure_ptr as *mut c_void)
288        }
289        if let Some(err) = closure.error {
290            panic!("Non-UTF-8 uniform name in ANGLE shader: {}", err)
291        }
292        closure.map
293    }
294
295    pub fn get_num_unpacked_varying_vectors(&self) -> i32 {
296        unsafe { GLSLangGetNumUnpackedVaryingVectors(self.handle) }
297    }
298}
299
300impl Drop for ShaderValidator {
301    fn drop(&mut self) {
302        unsafe { GLSLangDestructCompiler(self.handle) }
303    }
304}