From 274ba0ac6975c353ba5c79c92d6e6750b9b32043 Mon Sep 17 00:00:00 2001 From: ngohara Date: Mon, 30 Nov 2015 20:45:57 -0500 Subject: [PATCH] Support EGL on non-Android Linux. --- .travis.yml | 1 + Cargo.toml | 8 +- src/lib.rs | 7 +- src/platform/egl/surface.rs | 136 ++++++++++++++++++++++++++++++++++ src/platform/linux/surface.rs | 59 +++++++++++++-- src/platform/surface.rs | 30 +++++--- 6 files changed, 219 insertions(+), 22 deletions(-) create mode 100644 src/platform/egl/surface.rs diff --git a/.travis.yml b/.travis.yml index 133747a..8a71d62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ addons: packages: - libxxf86vm-dev - libosmesa6-dev + - libgles2-mesa-dev before_install: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update; fi diff --git a/Cargo.toml b/Cargo.toml index ceb7e80..24899c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "layers" -version = "0.2.3" +version = "0.2.4" authors = ["The Servo Project Developers"] license = "MIT/Apache-2.0" @@ -33,19 +33,23 @@ io-surface = "0.2.0" [target.i686-unknown-linux-gnu.dependencies] glx = "0.1.0" +servo-egl = "0.2" x11 = { version = "2.3.0", features = ["xlib"] } [target.x86_64-unknown-linux-gnu.dependencies] glx = "0.1.0" +servo-egl = "0.2" x11 = { version = "2.3.0", features = ["xlib"] } [target.arm-unknown-linux-gnueabihf.dependencies] glx = "0.1.0" +servo-egl = "0.2" x11 = { version = "2.3.0", features = ["xlib"] } [target.aarch64-unknown-linux-gnu.dependencies] glx = "0.1.0" +servo-egl = "0.2" x11 = { version = "2.3.0", features = ["xlib"] } [target.arm-linux-androideabi.dependencies] -servo-egl = "0.2" +servo-egl = "0.2" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index f02cffb..41f13cc 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,7 +40,7 @@ extern crate x11; #[cfg(target_os="linux")] extern crate glx; -#[cfg(target_os="android")] +#[cfg(any(target_os = "linux", target_os = "android"))] extern crate egl; pub mod color; @@ -65,10 +65,13 @@ pub mod platform { pub mod android { pub mod surface; } + #[cfg(any(target_os="android",target_os="linux"))] + pub mod egl { + pub mod surface; + } #[cfg(target_os="windows")] pub mod windows { pub mod surface; } pub mod surface; } - diff --git a/src/platform/egl/surface.rs b/src/platform/egl/surface.rs new file mode 100644 index 0000000..a1a2206 --- /dev/null +++ b/src/platform/egl/surface.rs @@ -0,0 +1,136 @@ +// Copyright 2015 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of cross-process surfaces implementing EGL surface. + +use texturegl::Texture; + +use egl::eglext::EGLImageKHR; +use euclid::size::Size2D; +use gleam::gl::{TEXTURE_2D, TexImage2D, UNSIGNED_BYTE}; +use skia::gl_context::GLContext; +use skia::gl_rasterization_context::GLRasterizationContext; +use std::iter::repeat; +use std::os::raw::c_void; +use std::sync::Arc; +use std::vec::Vec; + +use gleam::gl; + +#[cfg(target_os = "linux")] +const GL_FORMAT_BGRA: gl::GLuint = gl::BGRA; + +#[cfg(any(target_os = "android", target_os = "gonk"))] +const GL_FORMAT_BGRA: gl::GLuint = gl::BGRA_EXT; + +#[cfg(target_os="linux")] +pub use platform::linux::surface::NativeDisplay; + +#[cfg(target_os="android")] +pub use platform::android::surface::NativeDisplay; + +pub struct EGLImageNativeSurface { + /// An EGLImage for the case of GPU rendering. + image: Option, + + /// A heap-allocated bitmap for the case of CPU rendering. + bitmap: Option>, + + /// Whether this pixmap will leak if the destructor runs. This is for debugging purposes. + will_leak: bool, + + /// The size of this surface. + pub size: Size2D, +} + +unsafe impl Send for EGLImageNativeSurface {} + +impl EGLImageNativeSurface { + pub fn new(_: &NativeDisplay, size: Size2D) -> EGLImageNativeSurface { + let len = size.width * size.height * 4; + let bitmap: Vec = repeat(0).take(len as usize).collect(); + + EGLImageNativeSurface { + image: None, + bitmap: Some(bitmap), + will_leak: true, + size: size, + } + } + + /// This may only be called on the compositor side. + pub fn bind_to_texture(&self, _: &NativeDisplay, texture: &Texture) { + let _bound = texture.bind(); + match self.image { + None => match self.bitmap { + Some(ref bitmap) => { + let data = bitmap.as_ptr() as *const c_void; + unsafe { + TexImage2D(TEXTURE_2D, + 0, + GL_FORMAT_BGRA as i32, + self.size.width as i32, + self.size.height as i32, + 0, + GL_FORMAT_BGRA as u32, + UNSIGNED_BYTE, + data); + } + } + None => { + debug!("Cannot bind the buffer(CPU rendering), there is no bitmap"); + } + }, + Some(_image_khr) => { + panic!("TODO: Support GPU rasterizer path on EGL"); + } + } + } + + /// This may only be called on the painting side. + pub fn upload(&mut self, _: &NativeDisplay, data: &[u8]) { + match self.bitmap { + Some(ref mut bitmap) => { + bitmap.clear(); + bitmap.extend_from_slice(data); + } + None => { + debug!("Cannot upload the buffer(CPU rendering), there is no bitmap"); + } + } + } + + pub fn get_id(&self) -> isize { + match self.image { + None => 0, + Some(image_khr) => image_khr as isize, + } + } + + pub fn destroy(&mut self, _graphics_context: &NativeDisplay) { + if self.image.is_some() { + panic!("TODO: Support GPU rendering path on Android"); + } + self.mark_wont_leak() + } + + pub fn mark_will_leak(&mut self) { + self.will_leak = true + } + + pub fn mark_wont_leak(&mut self) { + self.will_leak = false + } + + pub fn gl_rasterization_context(&mut self, + _gl_context: Arc) + -> Option { + panic!("TODO: Support GL context on EGL"); + } +} diff --git a/src/platform/linux/surface.rs b/src/platform/linux/surface.rs index bb4a215..d510e5d 100644 --- a/src/platform/linux/surface.rs +++ b/src/platform/linux/surface.rs @@ -11,6 +11,10 @@ #![allow(non_snake_case)] +//TODO: Linking EGL here is probably wrong - should it be done in gleam / glutin etc? +#[link(name = "EGL")] +extern {} + use texturegl::Texture; use euclid::size::Size2D; @@ -26,6 +30,8 @@ use std::str; use std::sync::Arc; use x11::xlib; +use egl::egl::{EGLDisplay, GetCurrentDisplay}; + /// The display, visual info, and framebuffer configuration. This is needed in order to bind to a /// texture on the compositor side. This holds only a *weak* reference to the display and does not /// close it. @@ -34,12 +40,25 @@ use x11::xlib; /// to fix because the Display is given to us by the native windowing system, but we should fix it /// someday. /// FIXME(pcwalton): Mark nonsendable. + #[derive(Copy, Clone)] -pub struct NativeDisplay { +pub struct GLXDisplayInfo { pub display: *mut xlib::Display, visual_info: *mut xlib::XVisualInfo, framebuffer_configuration: Option, } +#[derive(Copy, Clone)] +pub struct EGLDisplayInfo { + pub display: EGLDisplay, +} + +#[derive(Copy, Clone)] +pub enum NativeDisplay { + EGL(EGLDisplayInfo), + GLX(GLXDisplayInfo), +} + + unsafe impl Send for NativeDisplay {} @@ -50,11 +69,11 @@ impl NativeDisplay { let (compositor_visual_info, frambuffer_configuration) = NativeDisplay::compositor_visual_info(display); - NativeDisplay { + NativeDisplay::GLX(GLXDisplayInfo { display: display, visual_info: compositor_visual_info, framebuffer_configuration: frambuffer_configuration, - } + }) } /// Chooses the compositor visual info using the same algorithm that the compositor uses. @@ -141,11 +160,22 @@ impl NativeDisplay { } pub fn platform_display_data(&self) -> PlatformDisplayData { - PlatformDisplayData { - display: self.display, - visual_info: self.visual_info, + match *self { + NativeDisplay::GLX(info) => { + PlatformDisplayData { + display: info.display, + visual_info: info.visual_info, + } + } + NativeDisplay::EGL(_) => unreachable!(), } } + + pub fn new_egl_display() -> NativeDisplay { + NativeDisplay::EGL(EGLDisplayInfo { + display: GetCurrentDisplay() + }) + } } #[derive(RustcDecodable, RustcEncodable)] @@ -170,7 +200,7 @@ impl Drop for PixmapNativeSurface { } impl PixmapNativeSurface { - pub fn new(display: &NativeDisplay, size: Size2D) -> PixmapNativeSurface { + pub fn new(display: &GLXDisplayInfo, size: Size2D) -> PixmapNativeSurface { unsafe { // Create the pixmap. let screen = xlib::XDefaultScreen(display.display); @@ -197,6 +227,11 @@ impl PixmapNativeSurface { // // FIXME(pcwalton): RAII for exception safety? unsafe { + let display = match display { + &NativeDisplay::GLX(info) => info, + &NativeDisplay::EGL(_) => unreachable!(), + }; + let pixmap_attributes = [ glx::TEXTURE_TARGET_EXT as i32, glx::TEXTURE_2D_EXT as i32, glx::TEXTURE_FORMAT_EXT as i32, glx::TEXTURE_FORMAT_RGBA_EXT as i32, @@ -228,6 +263,11 @@ impl PixmapNativeSurface { /// This may only be called on the painting side. pub fn upload(&mut self, display: &NativeDisplay, data: &[u8]) { unsafe { + let display = match display { + &NativeDisplay::GLX(info) => info, + &NativeDisplay::EGL(_) => unreachable!(), + }; + let image = xlib::XCreateImage(display.display, (*display.visual_info).visual, 32, @@ -259,6 +299,11 @@ impl PixmapNativeSurface { pub fn destroy(&mut self, display: &NativeDisplay) { unsafe { + let display = match display { + &NativeDisplay::GLX(info) => info, + &NativeDisplay::EGL(_) => unreachable!(), + }; + assert!(self.pixmap != 0); xlib::XFreePixmap(display.display, self.pixmap); self.mark_wont_leak() diff --git a/src/platform/surface.rs b/src/platform/surface.rs index 812c623..04ebfa2 100644 --- a/src/platform/surface.rs +++ b/src/platform/surface.rs @@ -30,9 +30,11 @@ pub use platform::linux::surface::{NativeDisplay, #[cfg(target_os="linux")] use std::ptr; +#[cfg(any(target_os="android",target_os="linux"))] +pub use platform::egl::surface::{EGLImageNativeSurface}; + #[cfg(target_os="android")] -pub use platform::android::surface::{NativeDisplay, - EGLImageNativeSurface}; +pub use platform::android::surface::NativeDisplay; #[cfg(target_os="windows")] pub use platform::windows::surface::NativeDisplay; @@ -43,7 +45,7 @@ pub enum NativeSurface { Pixmap(PixmapNativeSurface), #[cfg(target_os="macos")] IOSurface(IOSurfaceNativeSurface), -#[cfg(target_os="android")] +#[cfg(any(target_os="android",target_os="linux"))] EGLImage(EGLImageNativeSurface), } @@ -51,12 +53,19 @@ pub enum NativeSurface { impl NativeSurface { /// Creates a new native surface with uninitialized data. pub fn new(display: &NativeDisplay, size: Size2D) -> NativeSurface { - if display.display == ptr::null_mut() { - NativeSurface::MemoryBuffer(MemoryBufferNativeSurface::new(display, size)) - } else { - NativeSurface::Pixmap(PixmapNativeSurface::new(display, size)) + match display { + &NativeDisplay::EGL(_info) => { + NativeSurface::EGLImage(EGLImageNativeSurface::new(display, size)) + } + &NativeDisplay::GLX(info) => { + if info.display == ptr::null_mut() { + NativeSurface::MemoryBuffer(MemoryBufferNativeSurface::new(display, size)) + } else { + NativeSurface::Pixmap(PixmapNativeSurface::new(&info, size)) + } + } } - } + } } #[cfg(target_os="macos")] @@ -94,7 +103,7 @@ macro_rules! native_surface_method_with_mutability { #[cfg(target_os="macos")] NativeSurface::IOSurface($pattern) => $surface.$function_name($($argument), *), - #[cfg(target_os="android")] + #[cfg(any(target_os="android",target_os="linux"))] NativeSurface::EGLImage($pattern) => $surface.$function_name($($argument), *), } @@ -131,7 +140,7 @@ macro_rules! native_surface_property { NativeSurface::Pixmap(ref surface) => surface.$property_name, #[cfg(target_os="macos")] NativeSurface::IOSurface(ref surface) => surface.$property_name, - #[cfg(target_os="android")] + #[cfg(any(target_os="android",target_os="linux"))] NativeSurface::EGLImage(ref surface) => surface.$property_name, } }; @@ -267,4 +276,3 @@ impl MemoryBufferNativeSurface { None } } -