diff --git a/Cargo.toml b/Cargo.toml index 6b8c17d..3a90603 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "offscreen_gl_context" license = "MIT / Apache-2.0" -version = "0.8.6" +version = "0.8.7" authors = ["Emilio Cobos Álvarez ", "The Servo Project Developers"] description = "Creation and manipulation of HW accelerated offscreen rendering contexts in multiple platforms. Originally intended for the Servo project's WebGL implementation." repository = "https://github.com/emilio/rust-offscreen-rendering-context" diff --git a/src/draw_buffer.rs b/src/draw_buffer.rs index 3b879b0..e1b761c 100644 --- a/src/draw_buffer.rs +++ b/src/draw_buffer.rs @@ -49,15 +49,16 @@ impl ColorAttachment { /// This structure represents an offscreen context /// draw buffer. It has a framebuffer, with at least /// color renderbuffer (alpha or not). It may also have -/// a depth or stencil buffer, depending on context -/// requirements. +/// packed or independent depth or stencil buffers, +/// depending on context requirements. pub struct DrawBuffer { gl_: Rc, size: Size2D, framebuffer: GLuint, + color_attachment: Option, stencil_renderbuffer: GLuint, depth_renderbuffer: GLuint, - color_attachment: Option + packed_depth_stencil_renderbuffer: GLuint, // samples: GLsizei, } @@ -109,6 +110,7 @@ impl DrawBuffer { color_attachment: None, stencil_renderbuffer: 0, depth_renderbuffer: 0, + packed_depth_stencil_renderbuffer: 0, // samples: 0, }; @@ -204,14 +206,19 @@ impl DrawBuffer { }; // After this we check if we need stencil and depth buffers - if attrs.depth { - self.depth_renderbuffer = create_renderbuffer(self.gl(), formats.depth, &self.size); - debug_assert!(self.depth_renderbuffer != 0); - } - - if attrs.stencil { - self.stencil_renderbuffer = create_renderbuffer(self.gl(), formats.stencil, &self.size); - debug_assert!(self.stencil_renderbuffer != 0); + if attrs.depth && attrs.stencil && formats.packed_depth_stencil { + self.packed_depth_stencil_renderbuffer = create_renderbuffer(self.gl(), gl::DEPTH24_STENCIL8, &self.size); + debug_assert!(self.packed_depth_stencil_renderbuffer != 0); + } else { + if attrs.depth { + self.depth_renderbuffer = create_renderbuffer(self.gl(), formats.depth, &self.size); + debug_assert!(self.depth_renderbuffer != 0); + } + + if attrs.stencil { + self.stencil_renderbuffer = create_renderbuffer(self.gl(), formats.stencil, &self.size); + debug_assert!(self.stencil_renderbuffer != 0); + } } self.framebuffer = self.gl().gen_framebuffers(1)[0]; @@ -242,6 +249,14 @@ impl DrawBuffer { }, } + if self.packed_depth_stencil_renderbuffer != 0 { + self.gl().framebuffer_renderbuffer(gl::FRAMEBUFFER, + gl::DEPTH_STENCIL_ATTACHMENT, + gl::RENDERBUFFER, + self.packed_depth_stencil_renderbuffer); + debug_assert_eq!(self.gl().is_renderbuffer(self.packed_depth_stencil_renderbuffer), gl::TRUE); + } + if self.depth_renderbuffer != 0 { self.gl().framebuffer_renderbuffer(gl::FRAMEBUFFER, gl::DEPTH_ATTACHMENT, @@ -276,6 +291,8 @@ impl Drop for DrawBuffer { // NOTE: Color renderbuffer is destroyed on drop of // ColorAttachment - self.gl().delete_renderbuffers(&[self.stencil_renderbuffer, self.depth_renderbuffer]); + self.gl().delete_renderbuffers(&[self.stencil_renderbuffer, + self.depth_renderbuffer, + self.packed_depth_stencil_renderbuffer]); } } diff --git a/src/gl_context.rs b/src/gl_context.rs index d1876a1..d1ff182 100644 --- a/src/gl_context.rs +++ b/src/gl_context.rs @@ -25,6 +25,7 @@ pub struct GLContext { capabilities: GLContextCapabilities, formats: GLFormats, limits: GLLimits, + extensions: Vec } impl GLContext @@ -47,8 +48,10 @@ impl GLContext }; try!(native_context.make_current()); + let extensions = gl_.get_string(gl::EXTENSIONS); + let extensions: Vec = extensions.split(&[',',' '][..]).map(|s| s.into()).collect(); let attributes = GLContextAttributes::any(); - let formats = GLFormats::detect(&attributes, &*gl_); + let formats = GLFormats::detect(&attributes, &extensions[..]); let limits = GLLimits::detect(&*gl_); Ok(GLContext { @@ -59,6 +62,7 @@ impl GLContext capabilities: GLContextCapabilities::detect(), formats: formats, limits: limits, + extensions: extensions }) } @@ -100,7 +104,7 @@ impl GLContext shared_with, dispatcher)); - context.formats = GLFormats::detect(&attributes, context.gl()); + context.formats = GLFormats::detect(&attributes, &context.extensions[..]); context.attributes = attributes; try!(context.init_offscreen(size, color_attachment_type)); diff --git a/src/gl_formats.rs b/src/gl_formats.rs index 3dc2ecd..cadc944 100644 --- a/src/gl_formats.rs +++ b/src/gl_formats.rs @@ -10,7 +10,8 @@ pub struct GLFormats { pub texture: GLenum, pub texture_type: GLenum, pub depth: GLenum, - pub stencil: GLenum + pub stencil: GLenum, + pub packed_depth_stencil: bool, } impl GLFormats { @@ -20,7 +21,9 @@ impl GLFormats { // FIXME: In linux with GLES2 texture attachments create INVALID_ENUM errors. // I suspect that it's because of texture formats, but I need time to debugit. #[cfg(not(target_os="android"))] - pub fn detect(attrs: &GLContextAttributes, _: &gl::Gl) -> GLFormats { + pub fn detect(attrs: &GLContextAttributes, extensions: &[String]) -> GLFormats { + let packed_depth_stencil = GLFormats::supports_packed_depth_stencil(&extensions); + if attrs.alpha { GLFormats { color_renderbuffer: gl::RGBA8, @@ -29,6 +32,7 @@ impl GLFormats { texture_type: gl::UNSIGNED_BYTE, depth: gl::DEPTH_COMPONENT24, stencil: gl::STENCIL_INDEX8, + packed_depth_stencil: packed_depth_stencil, } } else { GLFormats { @@ -38,18 +42,19 @@ impl GLFormats { texture_type: gl::UNSIGNED_BYTE, depth: gl::DEPTH_COMPONENT24, stencil: gl::STENCIL_INDEX8, + packed_depth_stencil: packed_depth_stencil, } } } #[cfg(target_os="android")] - pub fn detect(attrs: &GLContextAttributes, gl: &gl::Gl) -> GLFormats { + pub fn detect(attrs: &GLContextAttributes, extensions: &[String]) -> GLFormats { // detect if the GPU supports RGB8 and RGBA8 renderbuffer/texture storage formats. // GL_ARM_rgba8 extension is similar to OES_rgb8_rgba8, but only exposes RGBA8. - let extensions = gl.get_string(gl::EXTENSIONS); - let extensions: Vec<&str> = extensions.split(&[',',' '][..]).collect(); - let has_rgb8 = extensions.contains(&"GL_OES_rgb8_rgba8"); - let has_rgba8 = has_rgb8 || extensions.contains(&"GL_ARM_rgba8"); + let has_rgb8 = extensions.iter().any(|s| s == "GL_OES_rgb8_rgba8"); + let has_rgba8 = has_rgb8 || extensions.iter().any(|s| s == "GL_ARM_rgba8"); + + let packed_depth_stencil = GLFormats::supports_packed_depth_stencil(&extensions); if attrs.alpha { GLFormats { @@ -59,6 +64,7 @@ impl GLFormats { texture_type: if has_rgba8 { gl::UNSIGNED_BYTE } else { gl::UNSIGNED_SHORT_4_4_4_4 }, depth: gl::DEPTH_COMPONENT16, stencil: gl::STENCIL_INDEX8, + packed_depth_stencil: packed_depth_stencil, } } else { GLFormats { @@ -68,8 +74,16 @@ impl GLFormats { texture_type: if has_rgb8 { gl::UNSIGNED_BYTE } else { gl::UNSIGNED_SHORT_4_4_4_4 }, depth: gl::DEPTH_COMPONENT16, stencil: gl::STENCIL_INDEX8, + packed_depth_stencil: packed_depth_stencil, } } } + + // Extension detection check to avoid incomplete framebuffers when using both depth and stencil buffers. + // Some implementations don't support separated DEPTH_COMPONENT and STENCIL_INDEX8 renderbuffers. + // Other implementations support only DEPTH24_STENCIL8 renderbuffer attachments. + fn supports_packed_depth_stencil(extensions: &[String]) -> bool { + extensions.iter().any(|s| s == "GL_OES_packed_depth_stencil" || s == "GL_EXT_packed_depth_stencil") + } } diff --git a/src/tests.rs b/src/tests.rs index 14ee199..c063c4c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -360,3 +360,35 @@ fn test_zero_size() { gl::GlType::default(), None).unwrap(); } + +#[test] +fn test_both_depth_stencil() { + let attributes = GLContextAttributes { + depth: true, + stencil: true, + .. Default::default() + }; + + let size = Size2D::new(256, 256); + GLContext::::new(size, + attributes, + ColorAttachmentType::Texture, + gl::GlType::default(), + None).unwrap(); +} + +#[test] +fn test_stencil_no_depth() { + let attributes = GLContextAttributes { + depth: false, + stencil: true, + .. Default::default() + }; + + let size = Size2D::new(256, 256); + GLContext::::new(size, + attributes, + ColorAttachmentType::Texture, + gl::GlType::default(), + None).unwrap(); +}