diff --git a/.travis.yml b/.travis.yml index a66ab24..e6bd4dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ addons: - g++-4.8 - libxxf86vm-dev - libosmesa6-dev + - libgles2-mesa-dev before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then export CXX=g++-4.8; fi @@ -21,7 +22,4 @@ os: script: - cargo build --verbose - - cargo build --features texture_surface --verbose - # FIXME(ecoal95): Travis' ubuntu doesn't have the neccessary graphics stack and it fails - if [ "$TRAVIS_OS_NAME" != "linux" ]; then cargo test --verbose; fi - - if [ "$TRAVIS_OS_NAME" != "linux" ]; then cargo test --features texture_surface --verbose; fi diff --git a/Cargo.toml b/Cargo.toml index c551101..7480c67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,6 @@ build = "build.rs" gl_generator = "0.4" khronos_api = "1.0" -[features] -texture_surface = ["layers"] - [dependencies] log = "0.3.3" gleam = "0.2" @@ -21,10 +18,6 @@ euclid = "0.4" serde = "0.6.1" serde_macros = "0.6.1" -[dependencies.layers] -git = "https://github.com/servo/rust-layers" -optional = true - [target.x86_64-apple-darwin.dependencies] core-foundation = "0.2.0" cgl = "0.1" @@ -44,6 +37,3 @@ features = ["xlib"] [target.aarch64-unknown-linux-gnu.dependencies.x11] version = "2.3.0" features = ["xlib"] - -[target.x86_64-pc-windows-gnu.dependencies] -servo-glutin = "0.4" diff --git a/build.rs b/build.rs index 5f8aacf..595045c 100644 --- a/build.rs +++ b/build.rs @@ -18,7 +18,7 @@ fn main() { "1.4", "core", &mut file).unwrap(); } - if target.contains("android") { + if target.contains("android") || target.contains("linux") { let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap(); gl_generator::generate_bindings(gl_generator::StaticGenerator, gl_generator::registry::Ns::Egl, diff --git a/src/draw_buffer.rs b/src/draw_buffer.rs index df3e195..567376b 100644 --- a/src/draw_buffer.rs +++ b/src/draw_buffer.rs @@ -3,22 +3,13 @@ use gleam::gl; use gleam::gl::types::{GLuint, GLenum, GLint}; use GLContext; +use NativeGLContextMethods; use std::ptr; -#[cfg(feature="texture_surface")] -use LayersSurfaceWrapper; -#[cfg(feature="texture_surface")] -use layers::texturegl::Texture; -#[cfg(feature="texture_surface")] -use layers::platform::surface::NativeSurface; - pub enum ColorAttachmentType { Texture, Renderbuffer, - - #[cfg(feature="texture_surface")] - TextureWithSurface, } impl ColorAttachmentType { @@ -35,9 +26,6 @@ impl ColorAttachmentType { pub enum ColorAttachment { Renderbuffer(GLuint), Texture(GLuint), - - #[cfg(feature="texture_surface")] - TextureWithSurface(LayersSurfaceWrapper, Texture), } impl ColorAttachment { @@ -45,8 +33,6 @@ impl ColorAttachment { match *self { ColorAttachment::Renderbuffer(_) => ColorAttachmentType::Renderbuffer, ColorAttachment::Texture(_) => ColorAttachmentType::Texture, - #[cfg(feature="texture_surface")] - ColorAttachment::TextureWithSurface(_, _) => ColorAttachmentType::TextureWithSurface, } } } @@ -57,10 +43,6 @@ impl Drop for ColorAttachment { match *self { ColorAttachment::Renderbuffer(mut id) => gl::DeleteRenderbuffers(1, &mut id), ColorAttachment::Texture(mut tex_id) => gl::DeleteTextures(1, &mut tex_id), - - #[cfg(feature="texture_surface")] - // Their destructors do everything - ColorAttachment::TextureWithSurface(_, _) => {}, } } } @@ -83,7 +65,8 @@ pub struct DrawBuffer { /// Helper function to create a render buffer /// TODO(ecoal95): We'll need to switch between `glRenderbufferStorage` and /// `glRenderbufferStorageMultisample` when we support antialising -fn create_renderbuffer(format: GLenum, size: &Size2D) -> GLuint { +fn create_renderbuffer(format: GLenum, + size: &Size2D) -> GLuint { let mut ret: GLuint = 0; unsafe { @@ -96,7 +79,9 @@ fn create_renderbuffer(format: GLenum, size: &Size2D) -> GLuint { } impl DrawBuffer { - pub fn new(context: &GLContext, size: Size2D, color_attachment_type: ColorAttachmentType) + pub fn new(context: &GLContext, + size: Size2D, + color_attachment_type: ColorAttachmentType) -> Result { let attrs = context.borrow_attributes(); @@ -125,7 +110,7 @@ impl DrawBuffer { unsafe { debug_assert!(gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE); - debug_assert!(gl::GetError() == gl::NO_ERROR); + debug_assert!(gl::get_error() == gl::NO_ERROR); } Ok(draw_buffer) @@ -159,38 +144,6 @@ impl DrawBuffer { match self.color_attachment.as_ref().unwrap() { &ColorAttachment::Renderbuffer(_) => None, &ColorAttachment::Texture(id) => Some(id), - #[cfg(feature="texture_surface")] - &ColorAttachment::TextureWithSurface(_, ref tex) => Some(tex.native_texture()), - } - } - - #[inline(always)] - #[cfg(feature="texture_surface")] - pub fn get_bound_surface_id(&self) -> Option { - match self.color_attachment.as_ref().unwrap() { - &ColorAttachment::TextureWithSurface(ref surf_wrapper, _) - => Some(surf_wrapper.get_surface_id()), - _ => None - } - } - - #[inline(always)] - #[cfg(feature="texture_surface")] - pub fn borrow_bound_layers_texture(&self) -> Option<&Texture> { - match self.color_attachment.as_ref().unwrap() { - &ColorAttachment::TextureWithSurface(_, ref tex) - => Some(tex), - _ => None - } - } - - #[inline(always)] - #[cfg(feature="texture_surface")] - pub fn borrow_bound_surface(&self) -> Option<&NativeSurface> { - match self.color_attachment.as_ref().unwrap() { - &ColorAttachment::TextureWithSurface(ref surf_wrapper, _) - => Some(surf_wrapper.borrow_surface()), - _ => None } } } @@ -217,14 +170,19 @@ impl Drop for DrawBuffer { } trait DrawBufferHelpers { - fn init(&mut self, &GLContext, color_attachment_type: ColorAttachmentType) + fn init(&mut self, + &GLContext, + color_attachment_type: ColorAttachmentType) -> Result<(), &'static str>; fn attach_to_framebuffer(&mut self) -> Result<(), &'static str>; } impl DrawBufferHelpers for DrawBuffer { - fn init(&mut self, context: &GLContext, color_attachment_type: ColorAttachmentType) -> Result<(), &'static str> { + fn init(&mut self, + context: &GLContext, + color_attachment_type: ColorAttachmentType) + -> Result<(), &'static str> { let attrs = context.borrow_attributes(); let formats = context.borrow_formats(); @@ -249,6 +207,7 @@ impl DrawBufferHelpers for DrawBuffer { gl::BindTexture(gl::TEXTURE_2D, texture); gl::TexImage2D(gl::TEXTURE_2D, 0, formats.texture_internal as GLint, self.size.width, self.size.height, 0, formats.texture, formats.texture_type, ptr::null_mut()); + // Low filtering to allow rendering gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint); @@ -256,25 +215,10 @@ impl DrawBufferHelpers for DrawBuffer { // TODO(ecoal95): Check if these two are neccessary, probably not gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint); + Some(ColorAttachment::Texture(texture)) } }, - #[cfg(feature="texture_surface")] - ColorAttachmentType::TextureWithSurface => { - // TODO(ecoal95): check if this is correct - let (flip, target) = Texture::texture_flip_and_target(false); - let mut texture = Texture::new(target, Size2D::new(self.size.width as usize, self.size.height as usize)); - texture.flip = flip; - - let surface_wrapper = LayersSurfaceWrapper::new(context.get_display(), self.size); - surface_wrapper.bind_to_texture(&texture); - - unsafe { - debug_assert!(gl::GetError() == gl::NO_ERROR); - } - - Some(ColorAttachment::TextureWithSurface(surface_wrapper, texture)) - } }; // After this we check if we need stencil and depth buffers @@ -317,13 +261,6 @@ impl DrawBufferHelpers for DrawBuffer { gl::TEXTURE_2D, texture_id, 0); }, - #[cfg(feature="texture_surface")] - &ColorAttachment::TextureWithSurface(_, ref texture) => { - gl::FramebufferTexture2D(gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - texture.target.as_gl_target(), - texture.native_texture(), 0); - } } if self.depth_renderbuffer != 0 { diff --git a/src/gl_context.rs b/src/gl_context.rs index ddf84c7..672159b 100644 --- a/src/gl_context.rs +++ b/src/gl_context.rs @@ -8,15 +8,10 @@ use GLContextCapabilities; use GLFormats; use DrawBuffer; use ColorAttachmentType; -use NativeGLContext; - -#[cfg(feature="texture_surface")] -use layers::platform::surface::NativeDisplay; - /// This is a wrapper over a native headless GL context -pub struct GLContext { - native_context: NativeGLContext, +pub struct GLContext { + native_context: Native, /// This an abstraction over a custom framebuffer /// with attachments according to WebGLContextAttributes // TODO(ecoal95): Ideally we may want a read and a draw @@ -28,17 +23,11 @@ pub struct GLContext { formats: GLFormats, } -impl GLContext { - #[inline(always)] - pub fn get_proc_address(addr: &str) -> *const () { - NativeGLContext::get_proc_address(addr) - } - - pub fn create_headless() -> Result { - let native_context = try!(NativeGLContext::create_headless()); - +impl GLContext + where Native: NativeGLContextMethods { + pub fn create(shared_with: Option<&Native::Handle>) -> Result, &'static str> { + let native_context = try!(Native::create_shared(shared_with)); try!(native_context.make_current()); - let attributes = GLContextAttributes::any(); let formats = GLFormats::detect(&attributes); @@ -51,15 +40,25 @@ impl GLContext { }) } - /// This allows to choose a color attachment type - /// create_offscreen() chooses the default one - pub fn create_offscreen_with_color_attachment(size: Size2D, - attributes: GLContextAttributes, - color_attachment_type: ColorAttachmentType) - -> Result { + + #[inline(always)] + pub fn get_proc_address(addr: &str) -> *const () { + Native::get_proc_address(addr) + } + + #[inline(always)] + pub fn current_handle() -> Option { + Native::current_handle() + } + + pub fn new(size: Size2D, + attributes: GLContextAttributes, + color_attachment_type: ColorAttachmentType, + shared_with: Option<&Native::Handle>) + -> Result, &'static str> { // We create a headless context with a dummy size, we're painting to the // draw_buffer's framebuffer anyways. - let mut context = try!(GLContext::create_headless()); + let mut context = try!(Self::create(shared_with)); context.formats = GLFormats::detect(&attributes); context.attributes = attributes; @@ -70,9 +69,11 @@ impl GLContext { } #[inline(always)] - pub fn create_offscreen(size: Size2D, attributes: GLContextAttributes) - -> Result { - GLContext::create_offscreen_with_color_attachment(size, attributes, ColorAttachmentType::default()) + pub fn with_default_color_attachment(size: Size2D, + attributes: GLContextAttributes, + shared_with: Option<&Native::Handle>) + -> Result, &'static str> { + GLContext::new(size, attributes, ColorAttachmentType::default(), shared_with) } #[inline(always)] @@ -80,11 +81,22 @@ impl GLContext { self.native_context.make_current() } + #[inline(always)] + pub fn unbind(&self) -> Result<(), &'static str> { + self.native_context.unbind() + } + #[inline(always)] pub fn is_current(&self) -> bool { self.native_context.is_current() } + #[inline(always)] + pub fn handle(&self) -> Native::Handle { + self.native_context.handle() + } + + // Allow borrowing these unmutably pub fn borrow_attributes(&self) -> &GLContextAttributes { &self.attributes @@ -128,11 +140,6 @@ impl GLContext { Err("No DrawBuffer found") } } - - #[cfg(feature="texture_surface")] - pub fn get_display(&self) -> NativeDisplay { - self.native_context.get_display() - } } @@ -141,7 +148,7 @@ trait GLContextPrivateMethods { fn create_draw_buffer(&mut self, Size2D, ColorAttachmentType) -> Result<(), &'static str>; } -impl GLContextPrivateMethods for GLContext { +impl GLContextPrivateMethods for GLContext { fn init_offscreen(&mut self, size: Size2D, color_attachment_type: ColorAttachmentType) -> Result<(), &'static str> { try!(self.create_draw_buffer(size, color_attachment_type)); diff --git a/src/gl_formats.rs b/src/gl_formats.rs index 6b3c8ff..87d19b0 100644 --- a/src/gl_formats.rs +++ b/src/gl_formats.rs @@ -15,13 +15,16 @@ pub struct GLFormats { impl GLFormats { // In the future we may use extension detection et-al to improve this, for now - // platform depending + // platform dependent. + // + // 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) -> GLFormats { if attrs.alpha { GLFormats { color_renderbuffer: gl::RGBA8, - texture_internal: gl::RGBA8, + texture_internal: gl::RGBA, texture: gl::RGBA, texture_type: gl::UNSIGNED_BYTE, depth: gl::DEPTH_COMPONENT24, diff --git a/src/layers_surface_wrapper.rs b/src/layers_surface_wrapper.rs deleted file mode 100644 index f586b91..0000000 --- a/src/layers_surface_wrapper.rs +++ /dev/null @@ -1,44 +0,0 @@ -use layers::platform::surface::{NativeSurface, NativeDisplay}; -use layers::texturegl::Texture; -use euclid::Size2D; - -/// A surface wrapper that owns the surface, -/// and thus destroys it on drop. We need a display -/// to create the surface and to bind it to a texture. -/// -/// Note that the GraphicsContext/CompositingContext -/// structs are not really GL context, just metadata -pub struct LayersSurfaceWrapper { - display: NativeDisplay, - surface: NativeSurface, -} - -impl LayersSurfaceWrapper { - pub fn new(display: NativeDisplay, size: Size2D) -> LayersSurfaceWrapper { - let mut surf = NativeSurface::new(&display, size); - surf.mark_will_leak(); - - LayersSurfaceWrapper { - display: display, - surface: surf, - } - } - - pub fn bind_to_texture(&self, texture: &Texture) { - self.surface.bind_to_texture(&self.display, texture) - } - - pub fn borrow_surface(&self) -> &NativeSurface { - &self.surface - } - - pub fn get_surface_id(&self) -> isize { - self.surface.get_id() - } -} - -impl Drop for LayersSurfaceWrapper { - fn drop(&mut self) { - self.surface.destroy(&self.display); - } -} diff --git a/src/lib.rs b/src/lib.rs index 0a2c0bc..9ab7904 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,11 +12,9 @@ extern crate x11; extern crate cgl; #[cfg(target_os="macos")] extern crate core_foundation; -#[cfg(target_os="windows")] -extern crate glutin; mod platform; -pub use platform::{NativeGLContext, NativeGLContextMethods}; +pub use platform::{NativeGLContext, NativeGLContextMethods, NativeGLContextHandle}; mod gl_context; pub use gl_context::GLContext; @@ -45,7 +43,7 @@ mod glx { include!(concat!(env!("OUT_DIR"), "/glx_bindings.rs")); } -#[cfg(target_os="android")] +#[cfg(any(target_os="linux", target_os="android"))] #[allow(non_camel_case_types)] mod egl { use std::os::raw::{c_long, c_void}; @@ -62,12 +60,5 @@ mod egl { include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); } -#[cfg(feature="texture_surface")] -extern crate layers; -#[cfg(feature="texture_surface")] -mod layers_surface_wrapper; -#[cfg(feature="texture_surface")] -pub use layers_surface_wrapper::LayersSurfaceWrapper; - #[cfg(test)] mod tests; diff --git a/src/platform/mod.rs b/src/platform/mod.rs index ef3ba22..de78496 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,44 +1,40 @@ -#[cfg(feature="texture_surface")] -use layers::platform::surface::NativeDisplay; - pub trait NativeGLContextMethods: Sized { + type Handle; + fn get_proc_address(&str) -> *const (); - fn create_headless() -> Result; + // These are convenient methods to manage handles + fn current() -> Option; + fn current_handle() -> Option; + + fn create_shared(with: Option<&Self::Handle>) -> Result; + + fn create_headless() -> Result { + Self::create_shared(None) + } + + fn handle(&self) -> Self::Handle; fn is_current(&self) -> bool; fn make_current(&self) -> Result<(), &'static str>; fn unbind(&self) -> Result<(), &'static str>; - - #[cfg(feature="texture_surface")] - fn get_display(&self) -> NativeDisplay; } #[cfg(target_os="linux")] pub mod with_glx; - #[cfg(target_os="linux")] -pub use self::with_glx::NativeGLContext; - -#[cfg(target_os="macos")] -pub mod with_cgl; +pub use self::with_glx::{NativeGLContext, NativeGLContextHandle}; -#[cfg(target_os="macos")] -pub use self::with_cgl::NativeGLContext; - -#[cfg(target_os="android")] +#[cfg(any(target_os="android", target_os="linux"))] pub mod with_egl; - #[cfg(target_os="android")] -pub use self::with_egl::NativeGLContext; - -#[cfg(target_os="windows")] -pub mod with_glutin; +pub use self::with_egl::{NativeGLContext, NativeGLContextHandle}; -#[cfg(target_os="windows")] -pub use self::with_glutin::NativeGLContext; +#[cfg(target_os="macos")] +pub mod with_cgl; +#[cfg(target_os="macos")] +pub use self::with_cgl::{NativeGLContext, NativeGLContextHandle}; -#[cfg(not(any(target_os="linux", target_os="macos", target_os="android", target_os="windows")))] +#[cfg(not(any(target_os="linux", target_os="android", target_os="macos")))] pub mod not_implemented; - -#[cfg(not(any(target_os="linux", target_os="macos", target_os="android", target_os="windows")))] -pub use self::not_implemented::NativeGLContext; +#[cfg(not(any(target_os="linux", target_os="android", target_os="macos")))] +pub use self::not_implemented::{NativeGLContext, NativeGLContextHandle}; diff --git a/src/platform/not_implemented/mod.rs b/src/platform/not_implemented/mod.rs index ea59b4e..fe3ce9a 100644 --- a/src/platform/not_implemented/mod.rs +++ b/src/platform/not_implemented/mod.rs @@ -1,2 +1,2 @@ mod native_gl_context; -pub use self::native_gl_context::NativeGLContext; +pub use self::native_gl_context::{NativeGLContext, NativeGLContextHandle}; diff --git a/src/platform/not_implemented/native_gl_context.rs b/src/platform/not_implemented/native_gl_context.rs index 23c08f2..e82dc7f 100644 --- a/src/platform/not_implemented/native_gl_context.rs +++ b/src/platform/not_implemented/native_gl_context.rs @@ -1,16 +1,16 @@ use NativeGLContextMethods; pub struct NativeGLContext; - -#[cfg(feature="texture_surface")] -use layers::platform::surface::NativeDisplay; +pub struct NativeGLContextHandle; impl NativeGLContextMethods for NativeGLContext { + type Handle = NativeGLContextHandle; + fn get_proc_address(_addr: &str) -> *const () { 0 as *const () } - fn create_headless() -> Result { + fn create_shared(_with: Option<&Self::Handle>) -> Result { Err("Not implemented (yet)") } @@ -18,6 +18,14 @@ impl NativeGLContextMethods for NativeGLContext { false } + fn current() -> Option { + None + } + + fn current_handle() -> Option { + None + } + fn make_current(&self) -> Result<(), &'static str> { Err("Not implemented (yet)") } @@ -26,8 +34,7 @@ impl NativeGLContextMethods for NativeGLContext { unimplemented!() } - #[cfg(feature="texture_surface")] - fn get_display(&self) -> NativeDisplay { + fn handle(&self) -> Self::Handle { unimplemented!() } } diff --git a/src/platform/with_cgl/mod.rs b/src/platform/with_cgl/mod.rs index ea59b4e..86ac835 100644 --- a/src/platform/with_cgl/mod.rs +++ b/src/platform/with_cgl/mod.rs @@ -1,2 +1,3 @@ mod native_gl_context; pub use self::native_gl_context::NativeGLContext; +pub use self::native_gl_context::NativeGLContextHandle; diff --git a/src/platform/with_cgl/native_gl_context.rs b/src/platform/with_cgl/native_gl_context.rs index fe2ab7a..5809272 100644 --- a/src/platform/with_cgl/native_gl_context.rs +++ b/src/platform/with_cgl/native_gl_context.rs @@ -8,28 +8,29 @@ use std::str::FromStr; use platform::NativeGLContextMethods; -#[cfg(feature="texture_surface")] -use layers::platform::surface::NativeDisplay; +pub struct NativeGLContextHandle(CGLContextObj); + +unsafe impl Send for NativeGLContextHandle {} pub struct NativeGLContext { native_context: CGLContextObj, - pixel_format: CGLPixelFormatObj, + weak: bool, } impl NativeGLContext { - pub fn new(share_context: Option, - pixel_format: CGLPixelFormatObj) + pub fn new(share_context: Option<&CGLContextObj>, + pixel_format: &CGLPixelFormatObj) -> Result { let shared = match share_context { - Some(ctx) => ctx.as_native_cgl_context(), + Some(ctx) => *ctx, None => 0 as CGLContextObj }; - let mut native = unsafe { mem::uninitialized() }; + let mut native: CGLContextObj = unsafe { mem::uninitialized() }; unsafe { - if CGLCreateContext(pixel_format, shared, &mut native) != 0 { + if CGLCreateContext(*pixel_format, shared, &mut native) != 0 { return Err("CGLCreateContext"); } } @@ -38,30 +39,27 @@ impl NativeGLContext { Ok(NativeGLContext { native_context: native, - pixel_format: pixel_format, + weak: false, }) } - - pub fn as_native_cgl_context(&self) -> CGLContextObj { - self.native_context - } } impl Drop for NativeGLContext { fn drop(&mut self) { let _ = self.unbind(); - unsafe { - if CGLDestroyContext(self.native_context) != 0 { - debug!("CGLDestroyContext returned an error"); - } - if CGLDestroyPixelFormat(self.pixel_format) != 0 { - debug!("CGLDestroyPixelformat errored"); + if !self.weak { + unsafe { + if CGLDestroyContext(self.native_context) != 0 { + debug!("CGLDestroyContext returned an error"); + } } } } } impl NativeGLContextMethods for NativeGLContext { + type Handle = NativeGLContextHandle; + fn get_proc_address(addr: &str) -> *const () { let symbol_name: CFString = FromStr::from_str(addr).unwrap(); let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap(); @@ -74,7 +72,28 @@ impl NativeGLContextMethods for NativeGLContext { symbol as *const () } - fn create_headless() -> Result { + fn current() -> Option { + if let Some(handle) = Self::current_handle() { + Some(NativeGLContext { + native_context: handle.0, + weak: true, + }) + } else { + None + } + + } + + fn current_handle() -> Option { + let current = unsafe { CGLGetCurrentContext() }; + if current != 0 as CGLContextObj { + Some(NativeGLContextHandle(current)) + } else { + None + } + } + + fn create_shared(with: Option<&Self::Handle>) -> Result { let mut attributes = [ 0 ]; @@ -92,7 +111,19 @@ impl NativeGLContextMethods for NativeGLContext { } } - NativeGLContext::new(None, pixel_format) + let result = NativeGLContext::new(with.map(|handle| &handle.0), &pixel_format); + + unsafe { + if CGLDestroyPixelFormat(pixel_format) != 0 { + debug!("CGLDestroyPixelformat errored"); + } + } + + result + } + + fn handle(&self) -> Self::Handle { + NativeGLContextHandle(self.native_context) } #[inline(always)] @@ -123,11 +154,4 @@ impl NativeGLContextMethods for NativeGLContext { } } } - - #[cfg(feature="texture_surface")] - fn get_display(&self) -> NativeDisplay { - NativeDisplay { - pixel_format: self.pixel_format, - } - } } diff --git a/src/platform/with_egl/mod.rs b/src/platform/with_egl/mod.rs index fac4744..4fcaa64 100644 --- a/src/platform/with_egl/mod.rs +++ b/src/platform/with_egl/mod.rs @@ -1,6 +1,7 @@ mod native_gl_context; mod utils; pub use self::native_gl_context::NativeGLContext; +pub use self::native_gl_context::NativeGLContextHandle; // NB: The last three zeros in egl attributes after the egl::EGL_NONE // are a workaround for workaround buggy implementations. diff --git a/src/platform/with_egl/native_gl_context.rs b/src/platform/with_egl/native_gl_context.rs index a3f8d52..698ed5c 100644 --- a/src/platform/with_egl/native_gl_context.rs +++ b/src/platform/with_egl/native_gl_context.rs @@ -5,26 +5,25 @@ use std::ffi::CString; use egl; use egl::types::{EGLint, EGLBoolean, EGLDisplay, EGLSurface, EGLConfig, EGLContext}; -#[cfg(feature="texture_surface")] -use layers::platform::surface::NativeDisplay; -#[cfg(feature="texture_surface")] -use std::mem; +pub struct NativeGLContextHandle(pub EGLDisplay, pub EGLSurface); +unsafe impl Send for NativeGLContextHandle {} pub struct NativeGLContext { native_display: EGLDisplay, native_surface: EGLSurface, native_context: EGLContext, + weak: bool, } impl NativeGLContext { - pub fn new(share_context: Option<&NativeGLContext>, + pub fn new(share_context: Option<&EGLContext>, display: EGLDisplay, surface: EGLSurface, config: EGLConfig) -> Result { let shared = match share_context { - Some(ctx) => ctx.as_native_egl_context(), + Some(ctx) => *ctx, None => egl::NO_CONTEXT as EGLContext, }; @@ -46,30 +45,30 @@ impl NativeGLContext { native_display: display, native_surface: surface, native_context: ctx, + weak: false, }) } - - #[inline(always)] - pub fn as_native_egl_context(&self) -> EGLContext { - self.native_context - } } impl Drop for NativeGLContext { fn drop(&mut self) { let _ = self.unbind(); - unsafe { - if egl::DestroySurface(self.native_display, self.native_surface) == 0 { - debug!("egl::DestroySurface failed"); - } - if egl::DestroyContext(self.native_display, self.native_context) == 0 { - debug!("egl::DestroyContext failed"); + if !self.weak { + unsafe { + if egl::DestroySurface(self.native_display, self.native_surface) == 0 { + debug!("egl::DestroySurface failed"); + } + if egl::DestroyContext(self.native_display, self.native_context) == 0 { + debug!("egl::DestroyContext failed"); + } } } } } impl NativeGLContextMethods for NativeGLContext { + type Handle = NativeGLContextHandle; + fn get_proc_address(addr: &str) -> *const () { unsafe { let addr = CString::new(addr.as_bytes()).unwrap().as_ptr(); @@ -80,7 +79,40 @@ impl NativeGLContextMethods for NativeGLContext { fn create_headless() -> Result { // We create a context with a dummy size, we can't rely on a // default framebuffer - create_pixel_buffer_backed_offscreen_context(Size2D::new(16, 16)) + create_pixel_buffer_backed_offscreen_context(Size2D::new(16, 16), None) + } + + fn create_shared(with: Option<&Self::Handle>) -> Result { + create_pixel_buffer_backed_offscreen_context(Size2D::new(16, 16), with) + } + + fn current_handle() -> Option { + let native_context = unsafe { egl::GetCurrentContext() }; + let native_display = unsafe { egl::GetCurrentDisplay() }; + + if native_context != egl::NO_CONTEXT && native_display != egl::NO_DISPLAY { + Some(NativeGLContextHandle(native_context, native_display)) + } else { + None + } + } + + + fn current() -> Option { + if let Some(handle) = Self::current_handle() { + let surface = unsafe { egl::GetCurrentSurface(egl::DRAW as EGLint) }; + + debug_assert!(surface != egl::NO_SURFACE); + + Some(NativeGLContext { + native_context: handle.0, + native_display: handle.1, + native_surface: surface, + weak: true, + }) + } else { + None + } } #[inline(always)] @@ -104,6 +136,10 @@ impl NativeGLContextMethods for NativeGLContext { } } + fn handle(&self) -> Self::Handle { + NativeGLContextHandle(self.native_context, self.native_display) + } + fn unbind(&self) -> Result<(), &'static str> { unsafe { if self.is_current() && @@ -117,12 +153,4 @@ impl NativeGLContextMethods for NativeGLContext { } } } - - #[cfg(feature="texture_surface")] - fn get_display(&self) -> NativeDisplay { - unsafe { - // FIXME: https://github.com/servo/servo/pull/6423#issuecomment-113282933 - NativeDisplay::new_with_display(mem::transmute(self.native_display)) - } - } } diff --git a/src/platform/with_egl/utils.rs b/src/platform/with_egl/utils.rs index 55ac7c5..851ce35 100644 --- a/src/platform/with_egl/utils.rs +++ b/src/platform/with_egl/utils.rs @@ -1,6 +1,6 @@ use std::mem; use euclid::Size2D; -use super::NativeGLContext; +use super::{NativeGLContext, NativeGLContextHandle}; use egl; use egl::types::{EGLNativeDisplayType, EGLDisplay, EGLConfig, EGLSurface, EGLint}; @@ -21,7 +21,9 @@ fn create_pbuffer_surface(display: EGLDisplay, config: EGLConfig, size: Size2D) -> Result { +pub fn create_pixel_buffer_backed_offscreen_context(size: Size2D, + shared_with: Option<&NativeGLContextHandle>) + -> Result { let attributes = [ egl::SURFACE_TYPE as EGLint, egl::PBUFFER_BIT as EGLint, egl::RENDERABLE_TYPE as EGLint, egl::OPENGL_ES2_BIT as EGLint, @@ -32,19 +34,43 @@ pub fn create_pixel_buffer_backed_offscreen_context(size: Size2D) -> Result egl::NONE as EGLint, 0, 0, 0, // see mod.rs ]; - // TODO: Check if we should use `egl::GetCurrentDisplay` instead - let display = unsafe { egl::GetDisplay(egl::DEFAULT_DISPLAY as EGLNativeDisplayType) }; + let (shared_with, display) = match shared_with { + Some(handle) => (Some(&handle.0), handle.1), + None => { + unsafe { + let display = egl::GetDisplay(egl::DEFAULT_DISPLAY as EGLNativeDisplayType); + + if display == (egl::NO_DISPLAY as EGLDisplay) { + return Err("egl::GetDisplay"); + } + + // TODO: Ensure this is correct. It seems it's refcounted, but not atomically, so + // we can't `Terminate` it on drop. + // + // It's the default display anyways so it is not a big problem. + if egl::Initialize(display, 0 as *mut _, 0 as *mut _) == 0 { + return Err("egl::Initialize"); + } + + (None, display) + } + } + }; + if display == (egl::NO_DISPLAY as EGLDisplay) { return Err("egl::GetDisplay"); } - let mut config : EGLConfig = unsafe { mem::uninitialized() }; let mut found_configs : EGLint = 0; unsafe { - if egl::ChooseConfig(display, attributes.as_ptr(), &mut config, 1, &mut found_configs) == 0 { + if egl::ChooseConfig(display, + attributes.as_ptr(), + &mut config, + 1, + &mut found_configs) == egl::FALSE as u32 { return Err("egl::ChooseConfig"); } } @@ -55,5 +81,5 @@ pub fn create_pixel_buffer_backed_offscreen_context(size: Size2D) -> Result let surface = try!(create_pbuffer_surface(display, config, size)); - NativeGLContext::new(None, display, surface, config) + NativeGLContext::new(shared_with, display, surface, config) } diff --git a/src/platform/with_glutin/mod.rs b/src/platform/with_glutin/mod.rs deleted file mode 100644 index a90c04a..0000000 --- a/src/platform/with_glutin/mod.rs +++ /dev/null @@ -1,56 +0,0 @@ -use platform::NativeGLContextMethods; -use glutin::{HeadlessContext, HeadlessRendererBuilder}; - -#[cfg(feature="texture_surface")] -use layers::platform::surface::NativeDisplay; - -#[cfg(not(feature="texture_surface"))] -struct NativeDisplay; - -#[cfg(not(feature="texture_surface"))] -impl NativeDisplay { - fn new() -> NativeDisplay { - NativeDisplay - } -} - - -pub struct NativeGLContext { - context: HeadlessContext, - display: NativeDisplay, -} - -impl NativeGLContextMethods for NativeGLContext { - fn get_proc_address(_addr: &str) -> *const () { - 0 as *const () - } - - fn create_headless() -> Result { - let builder = HeadlessRendererBuilder::new(128, 128); - let glutin_context = try!(builder.build().or(Err("Glutin Headless context creation error"))); - - Ok(NativeGLContext { - context: glutin_context, - display: NativeDisplay::new(), - }) - } - - fn is_current(&self) -> bool { - self.context.is_current() - } - - fn make_current(&self) -> Result<(), &'static str> { - unsafe { - self.context.make_current().or(Err("MakeCurrent failed")) - } - } - - fn unbind(&self) -> Result<(), &'static str> { - Ok(()) - } - - #[cfg(feature="texture_surface")] - fn get_display(&self) -> NativeDisplay { - self.display - } -} diff --git a/src/platform/with_glx/mod.rs b/src/platform/with_glx/mod.rs index f0cce82..a7d0673 100644 --- a/src/platform/with_glx/mod.rs +++ b/src/platform/with_glx/mod.rs @@ -1,3 +1,3 @@ mod utils; mod native_gl_context; -pub use self::native_gl_context::NativeGLContext; +pub use self::native_gl_context::{NativeGLContext, NativeGLContextHandle}; diff --git a/src/platform/with_glx/native_gl_context.rs b/src/platform/with_glx/native_gl_context.rs index 3dc9edc..a9507aa 100644 --- a/src/platform/with_glx/native_gl_context.rs +++ b/src/platform/with_glx/native_gl_context.rs @@ -9,30 +9,32 @@ use super::utils::{create_offscreen_pixmap_backed_context}; use platform::NativeGLContextMethods; -#[cfg(feature="texture_surface")] -use layers::platform::surface::NativeDisplay; +pub struct NativeGLContextHandle(pub GLXContext, pub *mut glx::types::Display); + +unsafe impl Send for NativeGLContextHandle {} pub struct NativeGLContext { native_context: GLXContext, native_display: *mut glx::types::Display, native_drawable: GLXDrawable, + weak: bool, } impl NativeGLContext { - pub fn new(share_context: Option<&NativeGLContext>, + pub fn new(share_context: Option<&GLXContext>, display: *mut glx::types::Display, drawable: GLXDrawable, framebuffer_config: GLXFBConfig) -> Result { let shared = match share_context { - Some(ctx) => ctx.as_native_glx_context(), - None => 0 as GLXContext + Some(ctx) => *ctx, + None => 0 as GLXContext, }; let native = unsafe { glx::CreateNewContext(display, framebuffer_config, glx::RGBA_TYPE as c_int, shared, 1 as glx::types::Bool) }; - if native == (0 as *const c_void) { + if native.is_null() { unsafe { glx::DestroyPixmap(display, drawable as GLXPixmap) }; return Err("Error creating native glx context"); } @@ -41,6 +43,7 @@ impl NativeGLContext { native_context: native, native_display: display, native_drawable: drawable, + weak: false, }) } @@ -53,15 +56,19 @@ impl Drop for NativeGLContext { fn drop(&mut self) { // Unbind the current context to free the resources // inmediately - let _ = self.unbind(); // We don't want to panic - unsafe { - glx::DestroyContext(self.native_display, self.native_context); - glx::DestroyPixmap(self.native_display, self.native_drawable as GLXPixmap); + if !self.weak { + let _ = self.unbind(); // We don't want to panic + unsafe { + glx::DestroyContext(self.native_display, self.native_context); + glx::DestroyPixmap(self.native_display, self.native_drawable as GLXPixmap); + } } } } impl NativeGLContextMethods for NativeGLContext { + type Handle = NativeGLContextHandle; + fn get_proc_address(addr: &str) -> *const () { let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); @@ -70,10 +77,34 @@ impl NativeGLContextMethods for NativeGLContext { } } - fn create_headless() -> Result { - // We create a context with a dummy size since in other platforms - // a default framebuffer is not bound - create_offscreen_pixmap_backed_context(Size2D::new(16, 16)) + fn current_handle() -> Option { + let current = unsafe { glx::GetCurrentContext() }; + let dpy = unsafe { glx::GetCurrentDisplay() }; + + if current.is_null() || dpy.is_null() { + None + } else { + Some(NativeGLContextHandle(current, dpy)) + } + } + + fn current() -> Option { + if let Some(handle) = Self::current_handle() { + unsafe { + Some(NativeGLContext { + native_context: handle.0, + native_display: handle.1, + native_drawable: glx::GetCurrentDrawable(), + weak: true, + }) + } + } else { + None + } + } + + fn create_shared(with: Option<&Self::Handle>) -> Result { + create_offscreen_pixmap_backed_context(Size2D::new(16, 16), with) } #[inline(always)] @@ -83,6 +114,10 @@ impl NativeGLContextMethods for NativeGLContext { } } + fn handle(&self) -> NativeGLContextHandle { + NativeGLContextHandle(self.native_context, self.native_display) + } + fn make_current(&self) -> Result<(), &'static str> { unsafe { if !self.is_current() && @@ -108,10 +143,4 @@ impl NativeGLContextMethods for NativeGLContext { } } } - - #[cfg(feature="texture_surface")] - fn get_display(&self) -> NativeDisplay { - NativeDisplay::new(self.native_display as *mut Display) - } } - diff --git a/src/platform/with_glx/utils.rs b/src/platform/with_glx/utils.rs index dd9da1b..8f32a8e 100644 --- a/src/platform/with_glx/utils.rs +++ b/src/platform/with_glx/utils.rs @@ -1,25 +1,26 @@ use glx; use x11::xlib::*; -use glx::types::{GLXDrawable}; +use glx::types::GLXDrawable; use std::os::raw::*; use euclid::Size2D; use NativeGLContext; +use NativeGLContextHandle; -struct ScopedXFree { +pub struct ScopedXFree { ptr: *mut T } impl ScopedXFree { #[inline(always)] - fn new(ptr: *mut T) -> ScopedXFree { + pub fn new(ptr: *mut T) -> ScopedXFree { ScopedXFree { ptr: ptr } } #[inline(always)] - fn as_ptr(&self) -> *mut T { + pub fn as_ptr(&self) -> *mut T { self.ptr } } @@ -48,12 +49,20 @@ unsafe fn get_visual_and_depth(s: *mut Screen, id: VisualID) -> Result<(*mut Vis // Almost directly ported from // https://dxr.mozilla.org/mozilla-central/source/gfx/gl/GLContextProviderGLX.cpp -pub fn create_offscreen_pixmap_backed_context(size: Size2D) -> Result { - let dpy = unsafe { XOpenDisplay(0 as *mut c_char) }; +pub fn create_offscreen_pixmap_backed_context(size: Size2D, shared_with: Option<&NativeGLContextHandle>) -> Result { + let (shared_with, dpy) = match shared_with { + Some(handle) => (Some(&handle.0), handle.1), + None => { + let dpy = unsafe { XOpenDisplay(0 as *mut c_char) as *mut glx::types::Display }; + + if dpy.is_null() { + return Err("glx::XOpenDisplay"); + } + + (None, dpy) + } + }; - if dpy.is_null() { - return Err("glx::XOpenDisplay"); - } // We try to get possible framebuffer configurations which // can be pixmap-backed and renderable @@ -66,8 +75,8 @@ pub fn create_offscreen_pixmap_backed_context(size: Size2D) -> Result) -> Result) -> Result) -> Result::get_proc_address(s) as *const _); + }); } -fn test_gl_context(context: &GLContext) { +fn test_gl_context(context: &GLContext) { context.make_current().unwrap(); gl::clear_color(1.0, 0.0, 0.0, 1.0); @@ -56,58 +51,93 @@ fn test_pixels(pixels: &[u8]) { } } + #[test] -fn test_default_color_attachment() { - load_gl(); - test_gl_context(&GLContext::create_offscreen(Size2D::new(256, 256), GLContextAttributes::default()).unwrap()); +fn test_unbinding() { + let ctx = GLContext::::new(Size2D::new(256, 256), + GLContextAttributes::default(), + ColorAttachmentType::Renderbuffer, + None).unwrap(); + + assert!(NativeGLContext::current_handle().is_some()); + + ctx.unbind().unwrap(); + assert!(NativeGLContext::current_handle().is_none()); } #[test] -fn test_texture_color_attachment() { +fn test_renderbuffer_color_attachment() { load_gl(); - test_gl_context(&GLContext::create_offscreen_with_color_attachment(Size2D::new(256, 256), GLContextAttributes::default(), ColorAttachmentType::Texture).unwrap()) + test_gl_context(&GLContext::::new(Size2D::new(256, 256), + GLContextAttributes::default(), + ColorAttachmentType::Renderbuffer, + None).unwrap()); } #[test] -#[cfg(feature="texture_surface")] -fn test_texture_surface_color_attachment() { +fn test_texture_color_attachment() { load_gl(); - let size : Size2D = Size2D::new(256, 256); - let ctx = GLContext::create_offscreen_with_color_attachment(size, GLContextAttributes::default(), ColorAttachmentType::TextureWithSurface).unwrap(); + let size = Size2D::new(256, 256); + let context = GLContext::::new(size, + GLContextAttributes::default(), + ColorAttachmentType::Texture, + None).unwrap(); + test_gl_context(&context); - test_gl_context(&ctx); // Get the bound texture and check we're painting on it - let texture = ctx.borrow_draw_buffer().unwrap().borrow_bound_layers_texture().unwrap(); - - // Bind the texture, get its pixels in rgba format and test - // if it has the surface contents - let _bound = texture.bind(); + let texture_id = context.borrow_draw_buffer().unwrap().get_bound_texture_id().unwrap(); + assert!(texture_id != 0); let mut vec = vec![0u8; (size.width * size.height * 4) as usize]; - unsafe { - gl::GetTexImage(texture.target.as_gl_target(), 0, gl::RGBA as u32, gl::UNSIGNED_BYTE, vec.as_mut_ptr() as *mut _); - assert!(gl::GetError() == gl::NO_ERROR); + gl::GetTexImage(gl::TEXTURE_2D, 0, gl::RGBA as u32, gl::UNSIGNED_BYTE, vec.as_mut_ptr() as *mut _); } + assert!(gl::get_error() == gl::NO_ERROR); test_pixels(&vec); +} + +#[test] +fn test_sharing() { + load_gl(); + + let size = Size2D::new(256, 256); + let primary = GLContext::::new(size, + GLContextAttributes::default(), + ColorAttachmentType::Texture, + None).unwrap(); + + let primary_texture_id = primary.borrow_draw_buffer().unwrap().get_bound_texture_id().unwrap(); + assert!(primary_texture_id != 0); - // Pick up the (in theory) painted surface - // And bind it to a new Texture, to check if we actually painted on it - let surface = ctx.borrow_draw_buffer().unwrap().borrow_bound_surface().unwrap(); - let (flip, target) = Texture::texture_flip_and_target(false); - let mut texture = Texture::new(target, Size2D::new(size.width as usize, size.height as usize)); - texture.flip = flip; - surface.bind_to_texture(&ctx.get_display(), &texture); + let secondary = GLContext::::new(size, + GLContextAttributes::default(), + ColorAttachmentType::Texture, + Some(&primary.handle())).unwrap(); - let _bound = texture.bind(); + // Paint the second context red + test_gl_context(&secondary); + + // Now the secondary context is bound, get the texture id, switch contexts, and check the + // texture is there. + let secondary_texture_id = secondary.borrow_draw_buffer().unwrap().get_bound_texture_id().unwrap(); + assert!(secondary_texture_id != 0); + + primary.make_current().unwrap(); + assert!(unsafe { gl::IsTexture(secondary_texture_id) != 0 }); let mut vec = vec![0u8; (size.width * size.height * 4) as usize]; + + // Ensure the old texture is bound, and bind the new one + assert!(gl::get_integer_v(gl::TEXTURE_BINDING_2D) == primary_texture_id as GLint); + + gl::bind_texture(gl::TEXTURE_2D, secondary_texture_id); unsafe { - gl::GetTexImage(texture.target.as_gl_target(), 0, gl::RGBA as u32, gl::UNSIGNED_BYTE, vec.as_mut_ptr() as *mut _); - assert!(gl::GetError() == gl::NO_ERROR); + gl::GetTexImage(gl::TEXTURE_2D, 0, gl::RGBA as u32, gl::UNSIGNED_BYTE, vec.as_mut_ptr() as *mut _); } + assert!(gl::get_error() == gl::NO_ERROR); + test_pixels(&vec); }