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 canvas_traits::webgl::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId}; 7 use canvas_traits::webgl::{WebGLMsgSender, WebGLResult, WebGLError}; 8 use canvas_traits::webgl::webgl_channel; 9 use dom::bindings::cell::DomRefCell; 10 use dom::bindings::codegen::Bindings::WebGLFramebufferBinding; 11 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; 12 use dom::bindings::reflector::reflect_dom_object; 13 use dom::bindings::root::{Dom, DomRoot}; 14 use dom::webglobject::WebGLObject; 15 use dom::webglrenderbuffer::WebGLRenderbuffer; 16 use dom::webgltexture::WebGLTexture; 17 use dom::window::Window; 18 use dom_struct::dom_struct; 19 use std::cell::Cell; 20 21 #[must_root] 22 #[derive(Clone, JSTraceable, MallocSizeOf)] 23 enum WebGLFramebufferAttachment { 24 Renderbuffer(Dom<WebGLRenderbuffer>), 25 Texture { texture: Dom<WebGLTexture>, level: i32 }, 26 } 27 28 #[dom_struct] 29 pub struct WebGLFramebuffer { 30 webgl_object: WebGLObject, 31 id: WebGLFramebufferId, 32 /// target can only be gl::FRAMEBUFFER at the moment 33 target: Cell<Option<u32>>, 34 is_deleted: Cell<bool>, 35 size: Cell<Option<(i32, i32)>>, 36 status: Cell<u32>, 37 #[ignore_malloc_size_of = "Defined in ipc-channel"] 38 renderer: WebGLMsgSender, 39 40 // The attachment points for textures and renderbuffers on this 41 // FBO. 42 color: DomRefCell<Option<WebGLFramebufferAttachment>>, 43 depth: DomRefCell<Option<WebGLFramebufferAttachment>>, 44 stencil: DomRefCell<Option<WebGLFramebufferAttachment>>, 45 depthstencil: DomRefCell<Option<WebGLFramebufferAttachment>>, 46 } 47 48 impl WebGLFramebuffer { new_inherited(renderer: WebGLMsgSender, id: WebGLFramebufferId) -> WebGLFramebuffer49 fn new_inherited(renderer: WebGLMsgSender, 50 id: WebGLFramebufferId) 51 -> WebGLFramebuffer { 52 WebGLFramebuffer { 53 webgl_object: WebGLObject::new_inherited(), 54 id: id, 55 target: Cell::new(None), 56 is_deleted: Cell::new(false), 57 renderer: renderer, 58 size: Cell::new(None), 59 status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED), 60 color: DomRefCell::new(None), 61 depth: DomRefCell::new(None), 62 stencil: DomRefCell::new(None), 63 depthstencil: DomRefCell::new(None), 64 } 65 } 66 maybe_new(window: &Window, renderer: WebGLMsgSender) -> Option<DomRoot<WebGLFramebuffer>>67 pub fn maybe_new(window: &Window, renderer: WebGLMsgSender) 68 -> Option<DomRoot<WebGLFramebuffer>> { 69 let (sender, receiver) = webgl_channel().unwrap(); 70 renderer.send(WebGLCommand::CreateFramebuffer(sender)).unwrap(); 71 72 let result = receiver.recv().unwrap(); 73 result.map(|fb_id| WebGLFramebuffer::new(window, renderer, fb_id)) 74 } 75 new(window: &Window, renderer: WebGLMsgSender, id: WebGLFramebufferId) -> DomRoot<WebGLFramebuffer>76 pub fn new(window: &Window, 77 renderer: WebGLMsgSender, 78 id: WebGLFramebufferId) 79 -> DomRoot<WebGLFramebuffer> { 80 reflect_dom_object(Box::new(WebGLFramebuffer::new_inherited(renderer, id)), 81 window, 82 WebGLFramebufferBinding::Wrap) 83 } 84 } 85 86 87 impl WebGLFramebuffer { id(&self) -> WebGLFramebufferId88 pub fn id(&self) -> WebGLFramebufferId { 89 self.id 90 } 91 bind(&self, target: u32)92 pub fn bind(&self, target: u32) { 93 // Update the framebuffer status on binding. It may have 94 // changed if its attachments were resized or deleted while 95 // we've been unbound. 96 self.update_status(); 97 98 self.target.set(Some(target)); 99 let cmd = WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Explicit(self.id)); 100 self.renderer.send(cmd).unwrap(); 101 } 102 delete(&self)103 pub fn delete(&self) { 104 if !self.is_deleted.get() { 105 self.is_deleted.set(true); 106 let _ = self.renderer.send(WebGLCommand::DeleteFramebuffer(self.id)); 107 } 108 } 109 is_deleted(&self) -> bool110 pub fn is_deleted(&self) -> bool { 111 self.is_deleted.get() 112 } 113 size(&self) -> Option<(i32, i32)>114 pub fn size(&self) -> Option<(i32, i32)> { 115 self.size.get() 116 } 117 update_status(&self)118 fn update_status(&self) { 119 let c = self.color.borrow(); 120 let z = self.depth.borrow(); 121 let s = self.stencil.borrow(); 122 let zs = self.depthstencil.borrow(); 123 let has_c = c.is_some(); 124 let has_z = z.is_some(); 125 let has_s = s.is_some(); 126 let has_zs = zs.is_some(); 127 let attachments = [&*c, &*z, &*s, &*zs]; 128 129 // From the WebGL spec, 6.6 ("Framebuffer Object Attachments"): 130 // 131 // "In the WebGL API, it is an error to concurrently attach 132 // renderbuffers to the following combinations of 133 // attachment points: 134 // 135 // DEPTH_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT 136 // STENCIL_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT 137 // DEPTH_ATTACHMENT + STENCIL_ATTACHMENT 138 // 139 // If any of the constraints above are violated, then: 140 // 141 // checkFramebufferStatus must return FRAMEBUFFER_UNSUPPORTED." 142 if (has_zs && (has_z || has_s)) || 143 (has_z && has_s) { 144 self.status.set(constants::FRAMEBUFFER_UNSUPPORTED); 145 return; 146 } 147 148 let mut fb_size = None; 149 for attachment in &attachments { 150 // Get the size of this attachment. 151 let size = match **attachment { 152 Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb)) => { 153 att_rb.size() 154 } 155 Some(WebGLFramebufferAttachment::Texture { texture: ref att_tex, level } ) => { 156 let info = att_tex.image_info_at_face(0, level as u32); 157 Some((info.width() as i32, info.height() as i32)) 158 } 159 None => None, 160 }; 161 162 // Make sure that, if we've found any other attachment, 163 // that the size matches. 164 if size.is_some() { 165 if fb_size.is_some() && size != fb_size { 166 self.status.set(constants::FRAMEBUFFER_INCOMPLETE_DIMENSIONS); 167 return; 168 } else { 169 fb_size = size; 170 } 171 } 172 } 173 self.size.set(fb_size); 174 175 if has_c || has_z || has_zs || has_s { 176 self.status.set(constants::FRAMEBUFFER_COMPLETE); 177 } else { 178 self.status.set(constants::FRAMEBUFFER_UNSUPPORTED); 179 } 180 } 181 check_status(&self) -> u32182 pub fn check_status(&self) -> u32 { 183 return self.status.get(); 184 } 185 renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()>186 pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> { 187 let binding = match attachment { 188 constants::COLOR_ATTACHMENT0 => &self.color, 189 constants::DEPTH_ATTACHMENT => &self.depth, 190 constants::STENCIL_ATTACHMENT => &self.stencil, 191 constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil, 192 _ => return Err(WebGLError::InvalidEnum), 193 }; 194 195 let rb_id = match rb { 196 Some(rb) => { 197 *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Renderbuffer(Dom::from_ref(rb))); 198 Some(rb.id()) 199 } 200 201 _ => { 202 *binding.borrow_mut() = None; 203 None 204 } 205 }; 206 207 self.renderer.send(WebGLCommand::FramebufferRenderbuffer(constants::FRAMEBUFFER, 208 attachment, 209 constants::RENDERBUFFER, 210 rb_id)).unwrap(); 211 212 self.update_status(); 213 Ok(()) 214 } 215 texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>, level: i32) -> WebGLResult<()>216 pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>, 217 level: i32) -> WebGLResult<()> { 218 let binding = match attachment { 219 constants::COLOR_ATTACHMENT0 => &self.color, 220 constants::DEPTH_ATTACHMENT => &self.depth, 221 constants::STENCIL_ATTACHMENT => &self.stencil, 222 constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil, 223 _ => return Err(WebGLError::InvalidEnum), 224 }; 225 226 let tex_id = match texture { 227 // Note, from the GLES 2.0.25 spec, page 113: 228 // "If texture is zero, then textarget and level are ignored." 229 Some(texture) => { 230 // From the GLES 2.0.25 spec, page 113: 231 // 232 // "level specifies the mipmap level of the texture image 233 // to be attached to the framebuffer and must be 234 // 0. Otherwise, INVALID_VALUE is generated." 235 if level != 0 { 236 return Err(WebGLError::InvalidValue); 237 } 238 239 // "If texture is not zero, then texture must either 240 // name an existing texture object with an target of 241 // textarget, or texture must name an existing cube 242 // map texture and textarget must be one of: 243 // TEXTURE_CUBE_MAP_POSITIVE_X, 244 // TEXTURE_CUBE_MAP_POSITIVE_Y, 245 // TEXTURE_CUBE_MAP_POSITIVE_Z, 246 // TEXTURE_CUBE_MAP_NEGATIVE_X, 247 // TEXTURE_CUBE_MAP_NEGATIVE_Y, or 248 // TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise, 249 // INVALID_OPERATION is generated." 250 let is_cube = match textarget { 251 constants::TEXTURE_2D => false, 252 253 constants::TEXTURE_CUBE_MAP_POSITIVE_X => true, 254 constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true, 255 constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true, 256 constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true, 257 constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true, 258 constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true, 259 260 _ => return Err(WebGLError::InvalidEnum), 261 }; 262 263 match texture.target() { 264 Some(constants::TEXTURE_CUBE_MAP) if is_cube => {} 265 Some(_) if !is_cube => {} 266 _ => return Err(WebGLError::InvalidOperation), 267 } 268 269 *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture { 270 texture: Dom::from_ref(texture), 271 level: level } 272 ); 273 274 Some(texture.id()) 275 } 276 277 _ => { 278 *binding.borrow_mut() = None; 279 None 280 } 281 }; 282 283 self.renderer.send(WebGLCommand::FramebufferTexture2D(constants::FRAMEBUFFER, 284 attachment, 285 textarget, 286 tex_id, 287 level)).unwrap(); 288 289 self.update_status(); 290 Ok(()) 291 } 292 with_matching_renderbuffers<F>(&self, rb: &WebGLRenderbuffer, mut closure: F) where F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>)293 fn with_matching_renderbuffers<F>(&self, rb: &WebGLRenderbuffer, mut closure: F) 294 where F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>) 295 { 296 let attachments = [&self.color, 297 &self.depth, 298 &self.stencil, 299 &self.depthstencil]; 300 301 for attachment in &attachments { 302 let matched = { 303 match *attachment.borrow() { 304 Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb)) 305 if rb.id() == att_rb.id() => true, 306 _ => false, 307 } 308 }; 309 310 if matched { 311 closure(attachment); 312 } 313 } 314 } 315 with_matching_textures<F>(&self, texture: &WebGLTexture, mut closure: F) where F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>)316 fn with_matching_textures<F>(&self, texture: &WebGLTexture, mut closure: F) 317 where F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>) 318 { 319 let attachments = [&self.color, 320 &self.depth, 321 &self.stencil, 322 &self.depthstencil]; 323 324 for attachment in &attachments { 325 let matched = { 326 match *attachment.borrow() { 327 Some(WebGLFramebufferAttachment::Texture { texture: ref att_texture, .. }) 328 if texture.id() == att_texture.id() => true, 329 _ => false, 330 } 331 }; 332 333 if matched { 334 closure(attachment); 335 } 336 } 337 } 338 detach_renderbuffer(&self, rb: &WebGLRenderbuffer)339 pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) { 340 self.with_matching_renderbuffers(rb, |att| { 341 *att.borrow_mut() = None; 342 self.update_status(); 343 }); 344 } 345 detach_texture(&self, texture: &WebGLTexture)346 pub fn detach_texture(&self, texture: &WebGLTexture) { 347 self.with_matching_textures(texture, |att| { 348 *att.borrow_mut() = None; 349 self.update_status(); 350 }); 351 } 352 invalidate_renderbuffer(&self, rb: &WebGLRenderbuffer)353 pub fn invalidate_renderbuffer(&self, rb: &WebGLRenderbuffer) { 354 self.with_matching_renderbuffers(rb, |_att| { 355 self.update_status(); 356 }); 357 } 358 invalidate_texture(&self, texture: &WebGLTexture)359 pub fn invalidate_texture(&self, texture: &WebGLTexture) { 360 self.with_matching_textures(texture, |_att| { 361 self.update_status(); 362 }); 363 } 364 target(&self) -> Option<u32>365 pub fn target(&self) -> Option<u32> { 366 self.target.get() 367 } 368 } 369 370 impl Drop for WebGLFramebuffer { drop(&mut self)371 fn drop(&mut self) { 372 self.delete(); 373 } 374 } 375