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 ¶ms).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 ¶ms).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