aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/webglframebuffer.rs
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2016-09-17 12:20:24 +0100
committerEric Anholt <eric@anholt.net>2016-10-25 22:18:29 -0700
commit6c10d5ca75d8d6064fa36c3ec3309fdd54e5f1c7 (patch)
treee4dbff0d9509c5da5dad0b5d461bab6a898384c9 /components/script/dom/webglframebuffer.rs
parent989c936e67b54105b7d9162553070e4fbfcd5853 (diff)
downloadservo-6c10d5ca75d8d6064fa36c3ec3309fdd54e5f1c7.tar.gz
servo-6c10d5ca75d8d6064fa36c3ec3309fdd54e5f1c7.zip
webgl: Add support for FBO attachments.
This allows many FBO tests to start running as their framebuffers start coming back as framebuffer complete.
Diffstat (limited to 'components/script/dom/webglframebuffer.rs')
-rw-r--r--components/script/dom/webglframebuffer.rs169
1 files changed, 164 insertions, 5 deletions
diff --git a/components/script/dom/webglframebuffer.rs b/components/script/dom/webglframebuffer.rs
index 22749832389..4dddfb047a0 100644
--- a/components/script/dom/webglframebuffer.rs
+++ b/components/script/dom/webglframebuffer.rs
@@ -4,15 +4,27 @@
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::CanvasMsg;
+use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
-use dom::bindings::js::Root;
+use dom::bindings::js::{HeapGCValue, JS, Root};
use dom::bindings::reflector::reflect_dom_object;
use dom::globalscope::GlobalScope;
use dom::webglobject::WebGLObject;
+use dom::webglrenderbuffer::WebGLRenderbuffer;
+use dom::webgltexture::WebGLTexture;
use ipc_channel::ipc::{self, IpcSender};
use std::cell::Cell;
-use webrender_traits::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId};
+use webrender_traits::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLResult, WebGLError};
+
+#[must_root]
+#[derive(JSTraceable, Clone, HeapSizeOf)]
+enum WebGLFramebufferAttachment {
+ Renderbuffer(JS<WebGLRenderbuffer>),
+ Texture(JS<WebGLTexture>),
+}
+
+impl HeapGCValue for WebGLFramebufferAttachment {}
#[dom_struct]
pub struct WebGLFramebuffer {
@@ -21,8 +33,16 @@ pub struct WebGLFramebuffer {
/// target can only be gl::FRAMEBUFFER at the moment
target: Cell<Option<u32>>,
is_deleted: Cell<bool>,
+ status: Cell<u32>,
#[ignore_heap_size_of = "Defined in ipc-channel"]
renderer: IpcSender<CanvasMsg>,
+
+ // The attachment points for textures and renderbuffers on this
+ // FBO.
+ color: DOMRefCell<Option<WebGLFramebufferAttachment>>,
+ depth: DOMRefCell<Option<WebGLFramebufferAttachment>>,
+ stencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
+ depthstencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
}
impl WebGLFramebuffer {
@@ -35,6 +55,11 @@ impl WebGLFramebuffer {
target: Cell::new(None),
is_deleted: Cell::new(false),
renderer: renderer,
+ status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED),
+ color: DOMRefCell::new(None),
+ depth: DOMRefCell::new(None),
+ stencil: DOMRefCell::new(None),
+ depthstencil: DOMRefCell::new(None),
}
}
@@ -80,10 +105,144 @@ impl WebGLFramebuffer {
self.is_deleted.get()
}
+ fn update_status(&self) {
+ let has_c = self.color.borrow().is_some();
+ let has_z = self.depth.borrow().is_some();
+ let has_s = self.stencil.borrow().is_some();
+ let has_zs = self.depthstencil.borrow().is_some();
+
+ // From the WebGL spec, 6.6 ("Framebuffer Object Attachments"):
+ //
+ // "In the WebGL API, it is an error to concurrently attach
+ // renderbuffers to the following combinations of
+ // attachment points:
+ //
+ // DEPTH_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
+ // STENCIL_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
+ // DEPTH_ATTACHMENT + STENCIL_ATTACHMENT
+ //
+ // If any of the constraints above are violated, then:
+ //
+ // checkFramebufferStatus must return FRAMEBUFFER_UNSUPPORTED."
+ if (has_zs && (has_z || has_s)) ||
+ (has_z && has_s) {
+ self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
+ return;
+ }
+
+ if has_c || has_z || has_zs || has_s {
+ self.status.set(constants::FRAMEBUFFER_COMPLETE);
+ } else {
+ self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
+ }
+ }
+
pub fn check_status(&self) -> u32 {
- // Until we build support for attaching renderbuffers or
- // textures, all user FBOs are incomplete.
- return constants::FRAMEBUFFER_UNSUPPORTED;
+ return self.status.get();
+ }
+
+ pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
+ let binding = match attachment {
+ constants::COLOR_ATTACHMENT0 => &self.color,
+ constants::DEPTH_ATTACHMENT => &self.depth,
+ constants::STENCIL_ATTACHMENT => &self.stencil,
+ constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
+ _ => return Err(WebGLError::InvalidEnum),
+ };
+
+ let rb_id = match rb {
+ Some(rb) => {
+ *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Renderbuffer(JS::from_ref(rb)));
+ Some(rb.id())
+ }
+
+ _ => {
+ *binding.borrow_mut() = None;
+ None
+ }
+ };
+
+ self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferRenderbuffer(constants::FRAMEBUFFER,
+ attachment,
+ constants::RENDERBUFFER,
+ rb_id))).unwrap();
+
+ self.update_status();
+ Ok(())
+ }
+
+ pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>,
+ level: i32) -> WebGLResult<()> {
+ let binding = match attachment {
+ constants::COLOR_ATTACHMENT0 => &self.color,
+ constants::DEPTH_ATTACHMENT => &self.depth,
+ constants::STENCIL_ATTACHMENT => &self.stencil,
+ constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
+ _ => return Err(WebGLError::InvalidEnum),
+ };
+
+ let tex_id = match texture {
+ // Note, from the GLES 2.0.25 spec, page 113:
+ // "If texture is zero, then textarget and level are ignored."
+ Some(texture) => {
+ *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture(JS::from_ref(texture)));
+
+ // From the GLES 2.0.25 spec, page 113:
+ //
+ // "level specifies the mipmap level of the texture image
+ // to be attached to the framebuffer and must be
+ // 0. Otherwise, INVALID_VALUE is generated."
+ if level != 0 {
+ return Err(WebGLError::InvalidValue);
+ }
+
+ // "If texture is not zero, then texture must either
+ // name an existing texture object with an target of
+ // textarget, or texture must name an existing cube
+ // map texture and textarget must be one of:
+ // TEXTURE_CUBE_MAP_POSITIVE_X,
+ // TEXTURE_CUBE_MAP_POSITIVE_Y,
+ // TEXTURE_CUBE_MAP_POSITIVE_Z,
+ // TEXTURE_CUBE_MAP_NEGATIVE_X,
+ // TEXTURE_CUBE_MAP_NEGATIVE_Y, or
+ // TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise,
+ // INVALID_OPERATION is generated."
+ let is_cube = match textarget {
+ constants::TEXTURE_2D => false,
+
+ constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
+ constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
+ constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
+ constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
+ constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
+ constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,
+
+ _ => return Err(WebGLError::InvalidEnum),
+ };
+
+ match texture.target() {
+ Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}
+ Some(_) if !is_cube => {}
+ _ => return Err(WebGLError::InvalidOperation),
+ }
+
+ Some(texture.id())
+ }
+
+ _ => {
+ *binding.borrow_mut() = None;
+ None
+ }
+ };
+
+ self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferTexture2D(constants::FRAMEBUFFER,
+ attachment,
+ textarget,
+ tex_id,
+ level))).unwrap();
+
+ self.update_status();
+ Ok(())
}
pub fn target(&self) -> Option<u32> {