1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
6 use angle::hl::{BuiltInResources, Output, ShaderValidator};
7 use canvas_traits::webgl::{WebGLSLVersion, WebGLVersion};
8 use canvas_traits::webgl::{webgl_channel, WebGLCommand, WebGLMsgSender, WebGLParameter, WebGLResult, WebGLShaderId};
9 use dom::bindings::cell::DomRefCell;
10 use dom::bindings::codegen::Bindings::WebGLShaderBinding;
11 use dom::bindings::reflector::reflect_dom_object;
12 use dom::bindings::root::DomRoot;
13 use dom::bindings::str::DOMString;
14 use dom::webgl_extensions::WebGLExtensions;
15 use dom::webgl_extensions::ext::oesstandardderivatives::OESStandardDerivatives;
16 use dom::webglobject::WebGLObject;
17 use dom::window::Window;
18 use dom_struct::dom_struct;
19 use std::cell::Cell;
20 use std::sync::{ONCE_INIT, Once};
21 
22 #[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
23 pub enum ShaderCompilationStatus {
24     NotCompiled,
25     Succeeded,
26     Failed,
27 }
28 
29 #[dom_struct]
30 pub struct WebGLShader {
31     webgl_object: WebGLObject,
32     id: WebGLShaderId,
33     gl_type: u32,
34     source: DomRefCell<Option<DOMString>>,
35     info_log: DomRefCell<Option<String>>,
36     is_deleted: Cell<bool>,
37     attached_counter: Cell<u32>,
38     compilation_status: Cell<ShaderCompilationStatus>,
39     #[ignore_malloc_size_of = "Defined in ipc-channel"]
40     renderer: WebGLMsgSender,
41 }
42 
43 static GLSLANG_INITIALIZATION: Once = ONCE_INIT;
44 
45 impl WebGLShader {
new_inherited(renderer: WebGLMsgSender, id: WebGLShaderId, shader_type: u32) -> WebGLShader46     fn new_inherited(renderer: WebGLMsgSender,
47                      id: WebGLShaderId,
48                      shader_type: u32)
49                      -> WebGLShader {
50         GLSLANG_INITIALIZATION.call_once(|| ::angle::hl::initialize().unwrap());
51         WebGLShader {
52             webgl_object: WebGLObject::new_inherited(),
53             id: id,
54             gl_type: shader_type,
55             source: DomRefCell::new(None),
56             info_log: DomRefCell::new(None),
57             is_deleted: Cell::new(false),
58             attached_counter: Cell::new(0),
59             compilation_status: Cell::new(ShaderCompilationStatus::NotCompiled),
60             renderer: renderer,
61         }
62     }
63 
maybe_new(window: &Window, renderer: WebGLMsgSender, shader_type: u32) -> Option<DomRoot<WebGLShader>>64     pub fn maybe_new(window: &Window,
65                      renderer: WebGLMsgSender,
66                      shader_type: u32)
67                      -> Option<DomRoot<WebGLShader>> {
68         let (sender, receiver) = webgl_channel().unwrap();
69         renderer.send(WebGLCommand::CreateShader(shader_type, sender)).unwrap();
70 
71         let result = receiver.recv().unwrap();
72         result.map(|shader_id| WebGLShader::new(window, renderer, shader_id, shader_type))
73     }
74 
new(window: &Window, renderer: WebGLMsgSender, id: WebGLShaderId, shader_type: u32) -> DomRoot<WebGLShader>75     pub fn new(window: &Window,
76                renderer: WebGLMsgSender,
77                id: WebGLShaderId,
78                shader_type: u32)
79                -> DomRoot<WebGLShader> {
80         reflect_dom_object(Box::new(WebGLShader::new_inherited(renderer, id, shader_type)),
81                            window,
82                            WebGLShaderBinding::Wrap)
83     }
84 }
85 
86 
87 impl WebGLShader {
id(&self) -> WebGLShaderId88     pub fn id(&self) -> WebGLShaderId {
89         self.id
90     }
91 
gl_type(&self) -> u3292     pub fn gl_type(&self) -> u32 {
93         self.gl_type
94     }
95 
96     /// glCompileShader
compile( &self, webgl_version: WebGLVersion, glsl_version: WebGLSLVersion, ext: &WebGLExtensions )97     pub fn compile(
98         &self,
99         webgl_version: WebGLVersion,
100         glsl_version: WebGLSLVersion,
101         ext: &WebGLExtensions
102     ) {
103         if self.compilation_status.get() != ShaderCompilationStatus::NotCompiled {
104             debug!("Compiling already compiled shader {}", self.id);
105         }
106 
107         if let Some(ref source) = *self.source.borrow() {
108             let mut params = BuiltInResources::default();
109             params.FragmentPrecisionHigh = 1;
110             params.OES_standard_derivatives = ext.is_enabled::<OESStandardDerivatives>() as i32;
111             let validator = match webgl_version {
112                 WebGLVersion::WebGL1 => {
113                     let output_format = if cfg!(any(target_os = "android", target_os = "ios")) {
114                         Output::Essl
115                     } else {
116                         Output::Glsl
117                     };
118                     ShaderValidator::for_webgl(self.gl_type,
119                                                output_format,
120                                                &params).unwrap()
121                 },
122                 WebGLVersion::WebGL2 => {
123                     let output_format = if cfg!(any(target_os = "android", target_os = "ios")) {
124                         Output::Essl
125                     } else {
126                         match (glsl_version.major, glsl_version.minor) {
127                             (1, 30) => Output::Glsl130,
128                             (1, 40) => Output::Glsl140,
129                             (1, 50) => Output::Glsl150Core,
130                             (3, 30) => Output::Glsl330Core,
131                             (4, 0) => Output::Glsl400Core,
132                             (4, 10) => Output::Glsl410Core,
133                             (4, 20) => Output::Glsl420Core,
134                             (4, 30) => Output::Glsl430Core,
135                             (4, 40) => Output::Glsl440Core,
136                             (4, _) => Output::Glsl450Core,
137                             _ => Output::Glsl140
138                         }
139                     };
140                     ShaderValidator::for_webgl2(self.gl_type,
141                                                output_format,
142                                                &params).unwrap()
143                 },
144             };
145 
146             match validator.compile_and_translate(&[source]) {
147                 Ok(translated_source) => {
148                     debug!("Shader translated: {}", translated_source);
149                     // NOTE: At this point we should be pretty sure that the compilation in the paint thread
150                     // will succeed.
151                     // It could be interesting to retrieve the info log from the paint thread though
152                     let msg = WebGLCommand::CompileShader(self.id, translated_source);
153                     self.renderer.send(msg).unwrap();
154                     self.compilation_status.set(ShaderCompilationStatus::Succeeded);
155                 },
156                 Err(error) => {
157                     self.compilation_status.set(ShaderCompilationStatus::Failed);
158                     debug!("Shader {} compilation failed: {}", self.id, error);
159                 },
160             }
161 
162             *self.info_log.borrow_mut() = Some(validator.info_log());
163             // TODO(emilio): More data (like uniform data) should be collected
164             // here to properly validate uniforms.
165             //
166             // This requires a more complex interface with ANGLE, using C++
167             // bindings and being extremely cautious about destructing things.
168         }
169     }
170 
171     /// Mark this shader as deleted (if it wasn't previously)
172     /// and delete it as if calling glDeleteShader.
173     /// Currently does not check if shader is attached
delete(&self)174     pub fn delete(&self) {
175         if !self.is_deleted.get() {
176             self.is_deleted.set(true);
177             let _ = self.renderer.send(WebGLCommand::DeleteShader(self.id));
178         }
179     }
180 
is_deleted(&self) -> bool181     pub fn is_deleted(&self) -> bool {
182         self.is_deleted.get()
183     }
184 
is_attached(&self) -> bool185     pub fn is_attached(&self) -> bool {
186         self.attached_counter.get() > 0
187     }
188 
increment_attached_counter(&self)189     pub fn increment_attached_counter(&self) {
190         self.attached_counter.set(self.attached_counter.get() + 1);
191     }
192 
decrement_attached_counter(&self)193     pub fn decrement_attached_counter(&self) {
194         assert!(self.attached_counter.get() > 0);
195         self.attached_counter.set(self.attached_counter.get() - 1);
196     }
197 
198     /// glGetShaderInfoLog
info_log(&self) -> Option<String>199     pub fn info_log(&self) -> Option<String> {
200         self.info_log.borrow().clone()
201     }
202 
203     /// glGetParameter
parameter(&self, param_id: u32) -> WebGLResult<WebGLParameter>204     pub fn parameter(&self, param_id: u32) -> WebGLResult<WebGLParameter> {
205         let (sender, receiver) = webgl_channel().unwrap();
206         self.renderer.send(WebGLCommand::GetShaderParameter(self.id, param_id, sender)).unwrap();
207         receiver.recv().unwrap()
208     }
209 
210     /// Get the shader source
source(&self) -> Option<DOMString>211     pub fn source(&self) -> Option<DOMString> {
212         self.source.borrow().clone()
213     }
214 
215     /// glShaderSource
set_source(&self, source: DOMString)216     pub fn set_source(&self, source: DOMString) {
217         *self.source.borrow_mut() = Some(source);
218     }
219 
successfully_compiled(&self) -> bool220     pub fn successfully_compiled(&self) -> bool {
221         self.compilation_status.get() == ShaderCompilationStatus::Succeeded
222     }
223 }
224 
225 impl Drop for WebGLShader {
drop(&mut self)226     fn drop(&mut self) {
227         assert_eq!(self.attached_counter.get(), 0);
228         self.delete();
229     }
230 }
231