aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/webglframebuffer.rs
diff options
context:
space:
mode:
authorMátyás Mustoha <matyas.mustoha@h-lab.eu>2020-02-20 13:32:38 +0100
committerMátyás Mustoha <matyas.mustoha@h-lab.eu>2020-02-21 11:04:30 +0100
commitb41805a9206f940737c2644f73d975ae35e38f99 (patch)
tree119787334ac1d38cb73cf3a1f7cae51aedf57c29 /components/script/dom/webglframebuffer.rs
parentf5ff38b87559cd25db44fa7bc6f2a9c4cb2264e0 (diff)
downloadservo-b41805a9206f940737c2644f73d975ae35e38f99.tar.gz
servo-b41805a9206f940737c2644f73d975ae35e38f99.zip
Add support for WebGL2 framebuffer attachments
Adds an initial implementation for the framebuffer attachments introduced with WebGL2 and the related enums and constrains checks.
Diffstat (limited to 'components/script/dom/webglframebuffer.rs')
-rw-r--r--components/script/dom/webglframebuffer.rs302
1 files changed, 192 insertions, 110 deletions
diff --git a/components/script/dom/webglframebuffer.rs b/components/script/dom/webglframebuffer.rs
index def5b36a27f..c2eb10fcd60 100644
--- a/components/script/dom/webglframebuffer.rs
+++ b/components/script/dom/webglframebuffer.rs
@@ -4,8 +4,8 @@
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
-use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
@@ -14,9 +14,10 @@ use crate::dom::webglrenderbuffer::WebGLRenderbuffer;
use crate::dom::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgltexture::WebGLTexture;
use crate::dom::xrsession::XRSession;
-use canvas_traits::webgl::{webgl_channel, WebGLError, WebGLResult};
+use canvas_traits::webgl::{webgl_channel, WebGLError, WebGLResult, WebGLVersion};
use canvas_traits::webgl::{WebGLCommand, WebGLFramebufferBindingRequest};
use canvas_traits::webgl::{WebGLFramebufferId, WebGLOpaqueFramebufferId};
+use canvas_traits::webgl::{WebGLRenderbufferId, WebGLTextureId};
use dom_struct::dom_struct;
use euclid::Size2D;
use std::cell::Cell;
@@ -29,6 +30,10 @@ pub enum CompleteForRendering {
MissingColorAttachment,
}
+fn log2(n: u32) -> u32 {
+ 31 - n.leading_zeros()
+}
+
#[unrooted_must_root_lint::must_root]
#[derive(Clone, JSTraceable, MallocSizeOf)]
enum WebGLFramebufferAttachment {
@@ -84,15 +89,15 @@ pub enum WebGLFramebufferAttachmentRoot {
#[dom_struct]
pub struct WebGLFramebuffer {
webgl_object: WebGLObject,
+ webgl_version: WebGLVersion,
id: WebGLFramebufferId,
- /// target can only be gl::FRAMEBUFFER at the moment
target: Cell<Option<u32>>,
is_deleted: Cell<bool>,
size: Cell<Option<(i32, i32)>>,
status: Cell<u32>,
// The attachment points for textures and renderbuffers on this
// FBO.
- color: DomRefCell<Option<WebGLFramebufferAttachment>>,
+ colors: Vec<DomRefCell<Option<WebGLFramebufferAttachment>>>,
depth: DomRefCell<Option<WebGLFramebufferAttachment>>,
stencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
depthstencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
@@ -106,12 +111,13 @@ impl WebGLFramebuffer {
fn new_inherited(context: &WebGLRenderingContext, id: WebGLFramebufferId) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
+ webgl_version: context.webgl_version(),
id: id,
target: Cell::new(None),
is_deleted: Cell::new(false),
size: Cell::new(None),
status: Cell::new(constants::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT),
- color: DomRefCell::new(None),
+ colors: vec![DomRefCell::new(None); context.limits().max_color_attachments as usize],
depth: DomRefCell::new(None),
stencil: DomRefCell::new(None),
depthstencil: DomRefCell::new(None),
@@ -213,91 +219,138 @@ impl WebGLFramebuffer {
self.size.get()
}
+ fn check_attachment_constraints(
+ &self,
+ attachment: &Option<WebGLFramebufferAttachment>,
+ constraints: &[u32],
+ fb_size: &mut Option<(i32, i32)>,
+ ) -> Result<(), u32> {
+ // Get the size of this attachment.
+ let (format, size) = match attachment {
+ Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb)) => {
+ (Some(att_rb.internal_format()), att_rb.size())
+ },
+ Some(WebGLFramebufferAttachment::Texture {
+ texture: ref att_tex,
+ level,
+ }) => match att_tex.image_info_at_face(0, *level as u32) {
+ Some(info) => (
+ Some(info.internal_format().as_gl_constant()),
+ Some((info.width() as i32, info.height() as i32)),
+ ),
+ None => return Err(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
+ },
+ None => (None, None),
+ };
+
+ // Make sure that, if we've found any other attachment,
+ // that the size matches.
+ if size.is_some() {
+ if fb_size.is_some() && size != *fb_size {
+ return Err(constants::FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
+ } else {
+ *fb_size = size;
+ }
+ }
+
+ if let Some(format) = format {
+ if constraints.iter().all(|c| *c != format) {
+ return Err(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
+ }
+ }
+
+ Ok(())
+ }
+
pub fn update_status(&self) {
- let c = self.color.borrow();
let z = self.depth.borrow();
let s = self.stencil.borrow();
let zs = self.depthstencil.borrow();
- let has_c = c.is_some();
let has_z = z.is_some();
let has_s = s.is_some();
let has_zs = zs.is_some();
- let attachments = [&*c, &*z, &*s, &*zs];
+ let attachments = [&*z, &*s, &*zs];
let attachment_constraints = [
&[
- constants::RGBA4,
- constants::RGB5_A1,
- constants::RGB565,
- constants::RGBA,
- constants::RGB,
+ constants::DEPTH_COMPONENT16,
+ constants::DEPTH_COMPONENT24,
+ constants::DEPTH_COMPONENT32F,
+ constants::DEPTH24_STENCIL8,
+ constants::DEPTH32F_STENCIL8,
+ ][..],
+ &[
+ constants::STENCIL_INDEX8,
+ constants::DEPTH24_STENCIL8,
+ constants::DEPTH32F_STENCIL8,
][..],
- &[constants::DEPTH_COMPONENT16][..],
- &[constants::STENCIL_INDEX8][..],
&[constants::DEPTH_STENCIL][..],
];
- // 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;
+ let is_supported = match self.webgl_version {
+ // From the WebGL 1.0 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."
+ WebGLVersion::WebGL1 => !(has_zs && (has_z || has_s)) && !(has_z && has_s),
+
+ // In WebGL 2.0, DEPTH_STENCIL_ATTACHMENT is considered an alias for
+ // DEPTH_ATTACHMENT + STENCIL_ATTACHMENT, i.e., the same image is attached to both DEPTH_ATTACHMENT
+ // and STENCIL_ATTACHMENT, overwriting the original images attached to the two attachment points.
+ // If different images are bound to the depth and stencil attachment points, checkFramebufferStatus
+ // returns FRAMEBUFFER_UNSUPPORTED, and getFramebufferAttachmentParameter with attachment of
+ // DEPTH_STENCIL_ATTACHMENT generates an INVALID_OPERATION error.
+ // -- WebGL 2.0 spec, 4.1.5 Framebuffer Object Attachments
+ WebGLVersion::WebGL2 => {
+ use WebGLFramebufferAttachment::{Renderbuffer, Texture};
+ match (&*z, &*s) {
+ (Some(Renderbuffer(a)), Some(Renderbuffer(b))) => a.id() == b.id(),
+ (Some(Texture { texture: a, .. }), Some(Texture { texture: b, .. })) => {
+ a.id() == b.id()
+ },
+ _ => !has_z || !has_s,
+ }
+ },
+ };
+ if !is_supported {
+ return self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
}
let mut fb_size = None;
+
for (attachment, constraints) in attachments.iter().zip(&attachment_constraints) {
- // Get the size of this attachment.
- let (format, size) = match **attachment {
- Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb)) => {
- (Some(att_rb.internal_format()), att_rb.size())
- },
- Some(WebGLFramebufferAttachment::Texture {
- texture: ref att_tex,
- level,
- }) => match att_tex.image_info_at_face(0, level as u32) {
- Some(info) => (
- Some(info.internal_format().as_gl_constant()),
- Some((info.width() as i32, info.height() as i32)),
- ),
- None => {
- self.status
- .set(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
- return;
- },
- },
- None => (None, None),
- };
-
- // Make sure that, if we've found any other attachment,
- // that the size matches.
- if size.is_some() {
- if fb_size.is_some() && size != fb_size {
- self.status
- .set(constants::FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
- return;
- } else {
- fb_size = size;
- }
+ if let Err(errnum) =
+ self.check_attachment_constraints(attachment, constraints, &mut fb_size)
+ {
+ return self.status.set(errnum);
}
+ }
- if let Some(format) = format {
- if constraints.iter().all(|c| *c != format) {
- self.status
- .set(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
- return;
- }
+ let color_constraints = &[
+ constants::RGBA4,
+ constants::RGB5_A1,
+ constants::RGB565,
+ constants::RGBA,
+ constants::RGB,
+ ][..];
+ let has_c = self.colors.iter().any(|att| att.borrow().is_some());
+ for attachment in self.colors.iter() {
+ let attachment = attachment.borrow();
+ if let Err(errnum) =
+ self.check_attachment_constraints(&*attachment, color_constraints, &mut fb_size)
+ {
+ return self.status.set(errnum);
}
}
+
self.size.set(fb_size);
if has_c || has_z || has_zs || has_s {
@@ -339,13 +392,12 @@ impl WebGLFramebuffer {
return CompleteForRendering::Complete;
}
- if self.color.borrow().is_none() {
+ if self.colors.iter().any(|att| att.borrow().is_none()) {
return CompleteForRendering::MissingColorAttachment;
}
if !self.is_initialized.get() {
let attachments = [
- (&self.color, constants::COLOR_BUFFER_BIT),
(&self.depth, constants::DEPTH_BUFFER_BIT),
(&self.stencil, constants::STENCIL_BUFFER_BIT),
(
@@ -362,6 +414,14 @@ impl WebGLFramebuffer {
}
}
}
+ for attachment in self.colors.iter() {
+ if let Some(ref att) = *attachment.borrow() {
+ if att.needs_initialization() {
+ att.mark_initialized();
+ clear_bits |= constants::COLOR_BUFFER_BIT;
+ }
+ }
+ }
self.upcast::<WebGLObject>()
.context()
.initialize_framebuffer(clear_bits);
@@ -397,7 +457,7 @@ impl WebGLFramebuffer {
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::FramebufferRenderbuffer(
- constants::FRAMEBUFFER,
+ self.target.get().unwrap(),
attachment,
constants::RENDERBUFFER,
rb_id,
@@ -436,7 +496,10 @@ impl WebGLFramebuffer {
attachment: u32,
) -> Option<&DomRefCell<Option<WebGLFramebufferAttachment>>> {
match attachment {
- constants::COLOR_ATTACHMENT0 => Some(&self.color),
+ constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
+ let idx = attachment - constants::COLOR_ATTACHMENT0;
+ self.colors.get(idx as usize)
+ },
constants::DEPTH_ATTACHMENT => Some(&self.depth),
constants::STENCIL_ATTACHMENT => Some(&self.stencil),
constants::DEPTH_STENCIL_ATTACHMENT => Some(&self.depthstencil),
@@ -455,7 +518,7 @@ impl WebGLFramebuffer {
WebGLFramebufferAttachment::Renderbuffer(ref rb) => {
rb.attach_to_framebuffer(self);
context.send_command(WebGLCommand::FramebufferRenderbuffer(
- constants::FRAMEBUFFER,
+ self.target.get().unwrap(),
attachment_point,
constants::RENDERBUFFER,
Some(rb.id()),
@@ -464,7 +527,7 @@ impl WebGLFramebuffer {
WebGLFramebufferAttachment::Texture { ref texture, level } => {
texture.attach_to_framebuffer(self);
context.send_command(WebGLCommand::FramebufferTexture2D(
- constants::FRAMEBUFFER,
+ self.target.get().unwrap(),
attachment_point,
texture.target().expect("missing texture target"),
Some(texture.id()),
@@ -517,15 +580,6 @@ impl WebGLFramebuffer {
// Note, from the GLES 2.0.25 spec, page 113:
// "If texture is zero, then textarget and level are ignored."
Some(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
@@ -556,6 +610,16 @@ impl WebGLFramebuffer {
_ => return Err(WebGLError::InvalidOperation),
}
+ let context = self.upcast::<WebGLObject>().context();
+ let max_tex_size = if is_cube {
+ context.limits().max_cube_map_tex_size
+ } else {
+ context.limits().max_tex_size
+ };
+ if level < 0 || level as u32 > log2(max_tex_size) {
+ return Err(WebGLError::InvalidValue);
+ }
+
*binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
texture: Dom::from_ref(texture),
level: level,
@@ -571,7 +635,7 @@ impl WebGLFramebuffer {
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::FramebufferTexture2D(
- constants::FRAMEBUFFER,
+ self.target.get().unwrap(),
attachment,
textarget,
tex_id,
@@ -591,57 +655,75 @@ impl WebGLFramebuffer {
where
F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>, u32),
{
+ let rb_id = rb.id();
let attachments = [
- (&self.color, constants::COLOR_ATTACHMENT0),
(&self.depth, constants::DEPTH_ATTACHMENT),
(&self.stencil, constants::STENCIL_ATTACHMENT),
(&self.depthstencil, constants::DEPTH_STENCIL_ATTACHMENT),
];
- for (attachment, name) in &attachments {
- let matched = {
- match *attachment.borrow() {
- Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb))
- if rb.id() == att_rb.id() =>
- {
- true
- },
- _ => false,
- }
- };
+ fn has_matching_id(
+ attachment: &DomRefCell<Option<WebGLFramebufferAttachment>>,
+ target: &WebGLRenderbufferId,
+ ) -> bool {
+ match *attachment.borrow() {
+ Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb)) => {
+ att_rb.id() == *target
+ },
+ _ => false,
+ }
+ }
- if matched {
+ for (attachment, name) in &attachments {
+ if has_matching_id(attachment, &rb_id) {
closure(attachment, *name);
}
}
+
+ for (idx, attachment) in self.colors.iter().enumerate() {
+ if has_matching_id(attachment, &rb_id) {
+ let name = constants::COLOR_ATTACHMENT0 + idx as u32;
+ closure(attachment, name);
+ }
+ }
}
fn with_matching_textures<F>(&self, texture: &WebGLTexture, mut closure: F)
where
F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>, u32),
{
+ let tex_id = texture.id();
let attachments = [
- (&self.color, constants::COLOR_ATTACHMENT0),
(&self.depth, constants::DEPTH_ATTACHMENT),
(&self.stencil, constants::STENCIL_ATTACHMENT),
(&self.depthstencil, constants::DEPTH_STENCIL_ATTACHMENT),
];
- for (attachment, name) in &attachments {
- let matched = {
- match *attachment.borrow() {
- Some(WebGLFramebufferAttachment::Texture {
- texture: ref att_texture,
- ..
- }) if texture.id() == att_texture.id() => true,
- _ => false,
- }
- };
+ fn has_matching_id(
+ attachment: &DomRefCell<Option<WebGLFramebufferAttachment>>,
+ target: &WebGLTextureId,
+ ) -> bool {
+ match *attachment.borrow() {
+ Some(WebGLFramebufferAttachment::Texture {
+ texture: ref att_texture,
+ ..
+ }) if att_texture.id() == *target => true,
+ _ => false,
+ }
+ }
- if matched {
+ for (attachment, name) in &attachments {
+ if has_matching_id(attachment, &tex_id) {
closure(attachment, *name);
}
}
+
+ for (idx, attachment) in self.colors.iter().enumerate() {
+ if has_matching_id(attachment, &tex_id) {
+ let name = constants::COLOR_ATTACHMENT0 + idx as u32;
+ closure(attachment, name);
+ }
+ }
}
pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) -> WebGLResult<()> {