diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2019-12-20 16:09:16 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-20 16:09:16 -0500 |
commit | 62899c0f52dd7ecbae89690109e2d50806a1ca9c (patch) | |
tree | feae175f0ed4cb874b99b1f0cc09cd9923e7c237 | |
parent | 352e2277eb2cd0a72393e20aac9ac40d1e44de44 (diff) | |
parent | b5943f5ab36b3ef718a7908a1b624f077b9ab79c (diff) | |
download | servo-62899c0f52dd7ecbae89690109e2d50806a1ca9c.tar.gz servo-62899c0f52dd7ecbae89690109e2d50806a1ca9c.zip |
Auto merge of #25334 - asajeffrey:gstplugins-misc-tidying-up, r=jdm
Gstreamer plugin running in wayland with surfman 0.2
<!-- Please describe your changes on the following line: -->
Tidying up the gstreamer plugin. The plugin now:
* uses surfman 0.2
* runs in wayland (but can't render WebGL content yet)
* gets its GL configuration from gstreamer
* uses GLsync if needed
---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #24843
- [X] These changes do not require tests because
<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
-rw-r--r-- | Cargo.lock | 56 | ||||
-rw-r--r-- | etc/taskcluster/decision_task.py | 6 | ||||
-rw-r--r-- | ports/gstplugin/Cargo.toml | 7 | ||||
-rw-r--r-- | ports/gstplugin/README.md | 7 | ||||
-rw-r--r-- | ports/gstplugin/logging.rs | 2 | ||||
-rw-r--r-- | ports/gstplugin/servowebsrc.rs | 370 | ||||
-rw-r--r-- | servo-tidy.toml | 5 |
7 files changed, 318 insertions, 135 deletions
diff --git a/Cargo.lock b/Cargo.lock index a34068606bd..74c26708e59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -478,8 +478,8 @@ dependencies = [ "raqote", "servo_config", "sparkle", - "surfman", - "surfman-chains", + "surfman 0.1.3", + "surfman-chains 0.2.1", "surfman-chains-api", "webrender", "webrender_api", @@ -3000,7 +3000,7 @@ dependencies = [ "sparkle", "style", "style_traits", - "surfman", + "surfman 0.1.3", "webdriver_server", "webgpu", "webrender", @@ -4816,8 +4816,8 @@ dependencies = [ "log", "servo-media", "sparkle", - "surfman", - "surfman-chains", + "surfman 0.2.0", + "surfman-chains 0.3.0", "surfman-chains-api", ] @@ -5501,6 +5501,33 @@ dependencies = [ ] [[package]] +name = "surfman" +version = "0.2.0" +source = "git+https://github.com/pcwalton/surfman?branch=multi#808e5c5906dbcc6707536c8bac8bcc9389b4e1eb" +dependencies = [ + "bitflags", + "cgl 0.3.2", + "cocoa 0.19.1", + "core-foundation", + "core-graphics", + "display-link", + "euclid", + "gl_generator 0.11.0", + "io-surface", + "lazy_static", + "libc", + "log", + "mach", + "objc", + "parking_lot", + "wayland-sys 0.24.0", + "winapi", + "winit", + "wio", + "x11", +] + +[[package]] name = "surfman-chains" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5510,7 +5537,20 @@ dependencies = [ "fnv", "log", "sparkle", - "surfman", + "surfman 0.1.3", + "surfman-chains-api", +] + +[[package]] +name = "surfman-chains" +version = "0.3.0" +source = "git+https://github.com/asajeffrey/surfman-chains?branch=multi#80a71b1a2df71ae70c3c194d0af40b8ebf72968a" +dependencies = [ + "euclid", + "fnv", + "log", + "sparkle", + "surfman 0.2.0", "surfman-chains-api", ] @@ -6420,8 +6460,8 @@ dependencies = [ "log", "openxr", "serde", - "surfman", - "surfman-chains", + "surfman 0.1.3", + "surfman-chains 0.2.1", "time", "webxr-api", "winapi", diff --git a/etc/taskcluster/decision_task.py b/etc/taskcluster/decision_task.py index 7c9b521dac8..a3d70653794 100644 --- a/etc/taskcluster/decision_task.py +++ b/etc/taskcluster/decision_task.py @@ -491,9 +491,9 @@ def windows_unit(cached=True): "mach smoketest --angle", "mach package --dev", "mach build --dev --libsimpleservo", - # We're getting link errors on windows, due to the x11 feature being - # enabled on gstreamer-gl. https://github.com/servo/media/pull/304/ - "mach build --dev --media-stack=dummy -p servo-gst-plugin", + # The GStreamer plugin currently doesn't support Windows + # https://github.com/servo/servo/issues/25353 + # "mach build --dev -p servo-gst-plugin", ) .with_artifacts("repo/target/debug/msi/Servo.exe", diff --git a/ports/gstplugin/Cargo.toml b/ports/gstplugin/Cargo.toml index 33c97e04648..04393709477 100644 --- a/ports/gstplugin/Cargo.toml +++ b/ports/gstplugin/Cargo.toml @@ -22,7 +22,7 @@ glib = { version = "0.8", features = ["subclassing"] } gstreamer = { version = "0.14", features = ["subclassing"] } gstreamer-base = { version = "0.14", features = ["subclassing"] } gstreamer-gl = { version = "0.14" } -gstreamer-gl-sys = { version = "0.8" } +gstreamer-gl-sys = { version = "0.8", features = ["wayland"] } gstreamer-sys = { version = "0.8" } gstreamer-video = { version = "0.14", features = ["subclassing"] } log = "0.4" @@ -30,10 +30,9 @@ lazy_static = "1.4" libservo = {path = "../../components/servo"} servo-media = {git = "https://github.com/servo/media"} sparkle = "0.1" -# NOTE: the sm-angle-default feature only enables angle on windows, not other platforms! -surfman = { version = "0.1", features = ["sm-angle-default", "sm-osmesa"] } +surfman = { git = "https://github.com/pcwalton/surfman", branch = "multi" } surfman-chains-api = "0.2" -surfman-chains = "0.2.1" +surfman-chains = { git = "https://github.com/asajeffrey/surfman-chains", branch = "multi" } [build-dependencies] gst-plugin-version-helper = "0.1" diff --git a/ports/gstplugin/README.md b/ports/gstplugin/README.md index ca9d30ed87d..a2827161c82 100644 --- a/ports/gstplugin/README.md +++ b/ports/gstplugin/README.md @@ -1,5 +1,10 @@ # A GStreamer plugin which runs servo +## Supported platforms + +* MacOS + CGL +* Linux + Wayland (currently no WebGL content) + ## Build ``` @@ -68,8 +73,6 @@ LD_LIBRARY_PATH=$PWD/support/linux/gstreamer/gst/lib \ ## Troubleshooting running the plugin -*Currently x11 support is broken!* - First try: ``` GST_PLUGIN_PATH=target/gstplugins \ diff --git a/ports/gstplugin/logging.rs b/ports/gstplugin/logging.rs index f4838ec015d..5f12bd1ad24 100644 --- a/ports/gstplugin/logging.rs +++ b/ports/gstplugin/logging.rs @@ -9,7 +9,7 @@ use lazy_static::lazy_static; lazy_static! { pub static ref CATEGORY: DebugCategory = - DebugCategory::new("servosrc", DebugColorFlags::empty(), Some("Servo")); + DebugCategory::new("servowebsrc", DebugColorFlags::empty(), Some("Servo")); } pub static LOGGER: ServoSrcLogger = ServoSrcLogger; diff --git a/ports/gstplugin/servowebsrc.rs b/ports/gstplugin/servowebsrc.rs index 57572e3b0d2..fc2320ad0ba 100644 --- a/ports/gstplugin/servowebsrc.rs +++ b/ports/gstplugin/servowebsrc.rs @@ -25,6 +25,7 @@ use glib::subclass::object::Property; use glib::subclass::simple::ClassStruct; use glib::subclass::types::ObjectSubclass; use glib::translate::FromGlibPtrBorrow; +use glib::translate::ToGlibPtr; use glib::value::Value; use glib::ParamSpec; use gstreamer::gst_element_error; @@ -55,6 +56,7 @@ use gstreamer_base::BaseSrcExt; use gstreamer_gl::GLContext; use gstreamer_gl::GLContextExt; use gstreamer_gl::GLContextExtManual; +use gstreamer_gl::GLSyncMeta; use gstreamer_gl_sys::gst_gl_context_thread_add; use gstreamer_gl_sys::gst_gl_texture_target_to_gl; use gstreamer_gl_sys::gst_is_gl_memory; @@ -63,6 +65,7 @@ use gstreamer_gl_sys::GstGLMemory; use gstreamer_video::VideoInfo; use log::debug; +use log::error; use log::info; use servo::compositing::windowing::AnimationState; @@ -80,14 +83,28 @@ use sparkle::gl; use sparkle::gl::types::GLuint; use sparkle::gl::Gl; -use surfman::platform::generic::universal::context::Context; -use surfman::platform::generic::universal::device::Device; +use surfman::connection::Connection as ConnectionAPI; +use surfman::device::Device as DeviceAPI; +use surfman::ContextAttributeFlags; +use surfman::ContextAttributes; +use surfman::GLApi; +use surfman::GLVersion; use surfman::SurfaceAccess; use surfman::SurfaceType; - use surfman_chains::SwapChain; use surfman_chains_api::SwapChainAPI; +// For the moment, we only support wayland and cgl. +#[cfg(target_os = "macos")] +use surfman::platform::macos::cgl::device::Device; +#[cfg(all(unix, not(target_os = "macos")))] +use surfman::platform::unix::wayland::device::Device; + +type Context = <Device as DeviceAPI>::Context; +type Connection = <Device as DeviceAPI>::Connection; +type NativeContext = <Device as DeviceAPI>::NativeContext; +type NativeConnection = <Connection as ConnectionAPI>::NativeConnection; + use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; @@ -102,10 +119,11 @@ use std::time::Instant; pub struct ServoWebSrc { sender: Sender<ServoWebSrcMsg>, - swap_chain: SwapChain, url: Mutex<Option<String>>, info: Mutex<Option<VideoInfo>>, buffer_pool: Mutex<Option<BufferPool>>, + gl_context: Mutex<Option<GLContext>>, + connection: Mutex<Option<Connection>>, // When did the plugin get created? start: Instant, // How long should each frame last? @@ -119,6 +137,7 @@ pub struct ServoWebSrc { struct ServoWebSrcGfx { device: Device, context: Context, + swap_chain: SwapChain<Device>, gl: Rc<Gl>, read_fbo: GLuint, draw_fbo: GLuint, @@ -135,10 +154,18 @@ thread_local! { static GFX_CACHE: RefCell<HashMap<GLContext, ServoWebSrcGfx>> = RefCell::new(HashMap::new()); } +struct ConnectionWhichImplementsDebug(Connection); + +impl std::fmt::Debug for ConnectionWhichImplementsDebug { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + "Connection".fmt(fmt) + } +} + #[derive(Debug)] enum ServoWebSrcMsg { - Start(ServoUrl), - GetSwapChain(Sender<SwapChain>), + Start(ConnectionWhichImplementsDebug, GLVersion, ServoUrl), + GetSwapChain(Sender<SwapChain<Device>>), Resize(Size2D<i32, DevicePixel>), Heartbeat, Stop, @@ -152,18 +179,35 @@ const DEFAULT_FRAME_DURATION: Duration = Duration::from_micros(16_667); struct ServoThread { receiver: Receiver<ServoWebSrcMsg>, - swap_chain: SwapChain, - gfx: Rc<RefCell<ServoWebSrcGfx>>, + swap_chain: SwapChain<Device>, + gfx: Rc<RefCell<ServoThreadGfx>>, servo: Servo<ServoWebSrcWindow>, } +struct ServoThreadGfx { + device: Device, + context: Context, + gl: Rc<Gl>, +} + impl ServoThread { fn new(receiver: Receiver<ServoWebSrcMsg>) -> Self { + let (connection, version, url) = match receiver.recv() { + Ok(ServoWebSrcMsg::Start(connection, version, url)) => (connection.0, version, url), + e => panic!("Failed to start ({:?})", e), + }; + info!( + "Created new servo thread (GL v{}.{} for {})", + version.major, version.minor, url + ); let embedder = Box::new(ServoWebSrcEmbedder); - let window = Rc::new(ServoWebSrcWindow::new()); + let window = Rc::new(ServoWebSrcWindow::new(connection, version)); let swap_chain = window.swap_chain.clone(); let gfx = window.gfx.clone(); - let servo = Servo::new(embedder, window); + let mut servo = Servo::new(embedder, window); + let id = TopLevelBrowsingContextId::new(); + servo.handle_events(vec![WindowEvent::NewBrowser(url, id)]); + Self { receiver, swap_chain, @@ -176,7 +220,7 @@ impl ServoThread { while let Ok(msg) = self.receiver.recv() { debug!("Servo thread handling message {:?}", msg); match msg { - ServoWebSrcMsg::Start(url) => self.new_browser(url), + ServoWebSrcMsg::Start(..) => error!("Already started"), ServoWebSrcMsg::GetSwapChain(sender) => sender .send(self.swap_chain.clone()) .expect("Failed to send swap chain"), @@ -188,12 +232,6 @@ impl ServoThread { self.servo.handle_events(vec![WindowEvent::Quit]); } - fn new_browser(&mut self, url: ServoUrl) { - let id = TopLevelBrowsingContextId::new(); - self.servo - .handle_events(vec![WindowEvent::NewBrowser(url, id)]); - } - fn resize(&mut self, size: Size2D<i32, DevicePixel>) { { let mut gfx = self.gfx.borrow_mut(); @@ -251,53 +289,57 @@ impl EventLoopWaker for ServoWebSrcEmbedder { } struct ServoWebSrcWindow { - swap_chain: SwapChain, - gfx: Rc<RefCell<ServoWebSrcGfx>>, + swap_chain: SwapChain<Device>, + gfx: Rc<RefCell<ServoThreadGfx>>, gl: Rc<dyn gleam::gl::Gl>, } impl ServoWebSrcWindow { - fn new() -> Self { - let version = surfman::GLVersion { major: 4, minor: 3 }; - let flags = surfman::ContextAttributeFlags::empty(); - let attributes = surfman::ContextAttributes { version, flags }; - - let connection = surfman::Connection::new().expect("Failed to create connection"); - let adapter = surfman::Adapter::default().expect("Failed to create adapter"); - let mut device = - surfman::Device::new(&connection, &adapter).expect("Failed to create device"); + fn new(connection: Connection, version: GLVersion) -> Self { + let flags = ContextAttributeFlags::DEPTH | + ContextAttributeFlags::STENCIL | + ContextAttributeFlags::ALPHA; + let attributes = ContextAttributes { version, flags }; + + let adapter = connection + .create_adapter() + .expect("Failed to create adapter"); + let mut device = connection + .create_device(&adapter) + .expect("Failed to create device"); let descriptor = device .create_context_descriptor(&attributes) .expect("Failed to create descriptor"); - let context = device + let mut context = device .create_context(&descriptor) .expect("Failed to create context"); - // This is a workaround for surfman having a different bootstrap API with Angle - #[cfg(target_os = "windows")] - let mut device = device; - #[cfg(not(target_os = "windows"))] - let mut device = Device::Hardware(device); - #[cfg(target_os = "windows")] - let mut context = context; - #[cfg(not(target_os = "windows"))] - let mut context = Context::Hardware(context); - - let gleam = - unsafe { gleam::gl::GlFns::load_with(|s| device.get_proc_address(&context, s)) }; - let gl = Gl::gl_fns(gl::ffi_gl::Gl::load_with(|s| { - device.get_proc_address(&context, s) - })); + let (gleam, gl) = unsafe { + match device.gl_api() { + GLApi::GL => ( + gleam::gl::GlFns::load_with(|s| device.get_proc_address(&context, s)), + Gl::gl_fns(gl::ffi_gl::Gl::load_with(|s| { + device.get_proc_address(&context, s) + })), + ), + GLApi::GLES => ( + gleam::gl::GlesFns::load_with(|s| device.get_proc_address(&context, s)), + Gl::gles_fns(gl::ffi_gles::Gles2::load_with(|s| { + device.get_proc_address(&context, s) + })), + ), + } + }; device .make_context_current(&mut context) .expect("Failed to make context current"); debug_assert_eq!(gl.get_error(), gl::NO_ERROR); - let access = SurfaceAccess::GPUCPU; + let access = SurfaceAccess::GPUOnly; let size = Size2D::new(512, 512); let surface_type = SurfaceType::Generic { size }; let surface = device - .create_surface(&mut context, access, &surface_type) + .create_surface(&mut context, access, surface_type) .expect("Failed to create surface"); device @@ -310,6 +352,8 @@ impl ServoWebSrcWindow { .framebuffer_object; gl.viewport(0, 0, size.width, size.height); gl.bind_framebuffer(gl::FRAMEBUFFER, fbo); + gl.clear_color(0.0, 0.0, 0.0, 1.0); + gl.clear(gl::COLOR_BUFFER_BIT); debug_assert_eq!( (gl.check_framebuffer_status(gl::FRAMEBUFFER), gl.get_error()), (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR) @@ -318,17 +362,12 @@ impl ServoWebSrcWindow { let swap_chain = SwapChain::create_attached(&mut device, &mut context, access) .expect("Failed to create swap chain"); - let read_fbo = gl.gen_framebuffers(1)[0]; - let draw_fbo = gl.gen_framebuffers(1)[0]; - device.make_no_context_current().unwrap(); - let gfx = Rc::new(RefCell::new(ServoWebSrcGfx { + let gfx = Rc::new(RefCell::new(ServoThreadGfx { device, context, gl, - read_fbo, - draw_fbo, })); Self { @@ -354,9 +393,9 @@ impl WindowMethods for ServoWebSrcWindow { ), (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR) ); - let _ = self - .swap_chain - .swap_buffers(&mut gfx.device, &mut gfx.context); + self.swap_chain + .swap_buffers(&mut gfx.device, &mut gfx.context) + .expect("Failed to swap buffers"); let fbo = gfx .device .context_surface_info(&gfx.context) @@ -381,6 +420,7 @@ impl WindowMethods for ServoWebSrcWindow { gfx.device .make_context_current(&mut gfx.context) .expect("Failed to make context current"); + debug!("EMBEDDER done make_context_current"); debug_assert_eq!( ( gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER), @@ -450,21 +490,21 @@ impl ObjectSubclass for ServoWebSrc { fn new() -> Self { let (sender, receiver) = crossbeam_channel::bounded(1); thread::spawn(move || ServoThread::new(receiver).run()); - let (acks, ackr) = crossbeam_channel::bounded(1); - let _ = sender.send(ServoWebSrcMsg::GetSwapChain(acks)); - let swap_chain = ackr.recv().expect("Failed to get swap chain"); let info = Mutex::new(None); let url = Mutex::new(None); let buffer_pool = Mutex::new(None); + let gl_context = Mutex::new(None); + let connection = Mutex::new(None); let start = Instant::now(); let frame_duration_micros = AtomicU64::new(DEFAULT_FRAME_DURATION.as_micros() as u64); let next_frame_micros = AtomicU64::new(0); Self { sender, - swap_chain, info, url, buffer_pool, + gl_context, + connection, start, frame_duration_micros, next_frame_micros, @@ -527,7 +567,9 @@ impl ObjectImpl for ServoWebSrc { impl ElementImpl for ServoWebSrc {} impl BaseSrcImpl for ServoWebSrc { - fn set_caps(&self, src: &BaseSrc, outcaps: &Caps) -> Result<(), LoggableError> { + fn set_caps(&self, _src: &BaseSrc, outcaps: &Caps) -> Result<(), LoggableError> { + info!("Setting caps {:?}", outcaps); + // Save the video info for later use let info = VideoInfo::from_caps(outcaps) .ok_or_else(|| gst_loggable_error!(CATEGORY, "Failed to get video info"))?; @@ -545,21 +587,15 @@ impl BaseSrcImpl for ServoWebSrc { .store(frame_duration_micros, Ordering::SeqCst); } - // Get the downstream GL context - let mut gst_gl_context = std::ptr::null_mut(); - let el = src.upcast_ref::<Element>(); - unsafe { - gstreamer_gl_sys::gst_gl_query_local_gl_context( - el.as_ptr(), - gstreamer_sys::GST_PAD_SRC, - &mut gst_gl_context, - ); - } - if gst_gl_context.is_null() { - return Err(gst_loggable_error!(CATEGORY, "Failed to get GL context")); - } - // Create a new buffer pool for GL memory + let gst_gl_context = self + .gl_context + .lock() + .unwrap() + .as_ref() + .expect("Set caps before starting") + .to_glib_none() + .0; let gst_gl_buffer_pool = unsafe { gstreamer_gl_sys::gst_gl_buffer_pool_new(gst_gl_context) }; if gst_gl_buffer_pool.is_null() { @@ -591,16 +627,63 @@ impl BaseSrcImpl for ServoWebSrc { false } - fn start(&self, _src: &BaseSrc) -> Result<(), ErrorMessage> { + fn start(&self, src: &BaseSrc) -> Result<(), ErrorMessage> { info!("Starting"); - let guard = self + + // Get the URL + let url_guard = self .url .lock() .map_err(|_| gst_error_msg!(ResourceError::Settings, ["Failed to lock mutex"]))?; - let url = guard.as_ref().map(|s| &**s).unwrap_or(DEFAULT_URL); - let url = ServoUrl::parse(url) + let url_string = url_guard.as_ref().map(|s| &**s).unwrap_or(DEFAULT_URL); + let url = ServoUrl::parse(url_string) .map_err(|_| gst_error_msg!(ResourceError::Settings, ["Failed to parse url"]))?; - let _ = self.sender.send(ServoWebSrcMsg::Start(url)); + + // Get the downstream GL context + let mut gst_gl_context = std::ptr::null_mut(); + let el = src.upcast_ref::<Element>(); + unsafe { + gstreamer_gl_sys::gst_gl_query_local_gl_context( + el.as_ptr(), + gstreamer_sys::GST_PAD_SRC, + &mut gst_gl_context, + ); + } + if gst_gl_context.is_null() { + return Err(gst_error_msg!( + ResourceError::Settings, + ["Failed to get GL context"] + )); + } + let gl_context = unsafe { GLContext::from_glib_borrow(gst_gl_context) }; + let gl_version = gl_context.get_gl_version(); + let version = GLVersion { + major: gl_version.0 as u8, + minor: gl_version.1 as u8, + }; + + // Get the surfman connection on the GL thread + let mut task = BootstrapSurfmanOnGLThread { + servo_web_src: self, + result: None, + }; + + let data = &mut task as *mut BootstrapSurfmanOnGLThread as *mut c_void; + unsafe { + gst_gl_context_thread_add(gst_gl_context, Some(bootstrap_surfman_on_gl_thread), data) + }; + let connection = task.result.expect("Failed to get connection"); + + // Save the GL context and connection for later use + *self.gl_context.lock().expect("Poisoned lock") = Some(gl_context); + *self.connection.lock().expect("Poisoned lock") = Some(connection.clone()); + + // Inform servo we're starting + let _ = self.sender.send(ServoWebSrcMsg::Start( + ConnectionWhichImplementsDebug(connection), + version, + url, + )); Ok(()) } @@ -634,10 +717,12 @@ impl BaseSrcImpl for ServoWebSrc { // Activate the pool if necessary if !pool.is_active() { + debug!("Activating the buffer pool"); pool.set_active(true).map_err(|_| FlowError::Error)?; } // Get a buffer to fill + debug!("Acquiring a buffer"); let buffer = pool.acquire_buffer(None)?; // Get the GL memory from the buffer @@ -657,23 +742,55 @@ impl BaseSrcImpl for ServoWebSrc { // Fill the buffer on the GL thread let result = Err(FlowError::Error); - let mut task = RunOnGLThread { + let mut task = FillOnGLThread { servo_web_src: self, src, gl_memory, result, }; - let data = &mut task as *mut RunOnGLThread as *mut c_void; + + let data = &mut task as *mut FillOnGLThread as *mut c_void; unsafe { gst_gl_context_thread_add(gl_memory.mem.context, Some(fill_on_gl_thread), data) }; task.result?; + // Put down a GL sync point if needed + if let Some(meta) = buffer.get_meta::<GLSyncMeta>() { + let gl_context = unsafe { GLContext::from_glib_borrow(gl_memory.mem.context) }; + meta.set_sync_point(&gl_context); + } + // Wake up Servo let _ = self.sender.send(ServoWebSrcMsg::Heartbeat); Ok(buffer) } } -struct RunOnGLThread<'a> { +struct BootstrapSurfmanOnGLThread<'a> { + servo_web_src: &'a ServoWebSrc, + result: Option<Connection>, +} + +unsafe extern "C" fn bootstrap_surfman_on_gl_thread(context: *mut GstGLContext, data: *mut c_void) { + let task = &mut *(data as *mut BootstrapSurfmanOnGLThread); + let gl_context = GLContext::from_glib_borrow(context); + task.result = task.servo_web_src.bootstrap_surfman(gl_context); +} + +impl ServoWebSrc { + // Runs on the GL thread + fn bootstrap_surfman(&self, gl_context: GLContext) -> Option<Connection> { + gl_context + .activate(true) + .expect("Failed to activate GL context"); + let native_connection = + NativeConnection::current().expect("Failed to bootstrap native connection"); + let connection = unsafe { Connection::from_native_connection(native_connection) } + .expect("Failed to bootstrap surfman connection"); + Some(connection) + } +} + +struct FillOnGLThread<'a> { servo_web_src: &'a ServoWebSrc, src: &'a BaseSrc, gl_memory: &'a GstGLMemory, @@ -681,7 +798,7 @@ struct RunOnGLThread<'a> { } unsafe extern "C" fn fill_on_gl_thread(context: *mut GstGLContext, data: *mut c_void) { - let task = &mut *(data as *mut RunOnGLThread); + let task = &mut *(data as *mut FillOnGLThread); let gl_context = GLContext::from_glib_borrow(context); task.result = task .servo_web_src @@ -713,32 +830,51 @@ impl ServoWebSrc { let mut gfx_cache = gfx_cache.borrow_mut(); let gfx = gfx_cache.entry(gl_context.clone()).or_insert_with(|| { debug!("Bootstrapping surfman"); - let (device, context) = unsafe { surfman::Device::from_current_context() } - .expect("Failed to bootstrap surfman"); - - // This is a workaround for surfman having a different bootstrap API with Angle - #[cfg(not(target_os = "windows"))] - let device = Device::Hardware(device); - #[cfg(not(target_os = "windows"))] - let context = Context::Hardware(context); - + let connection_guard = self.connection.lock().unwrap(); + let connection = connection_guard.as_ref().expect("Failed to get surfman"); + let adapter = connection + .create_adapter() + .expect("Failed to bootstrap surfman adapter"); + let device = connection + .create_device(&adapter) + .expect("Failed to bootstrap surfman device"); + let native_context = + NativeContext::current().expect("Failed to bootstrap native context"); + let context = unsafe { + device + .create_context_from_native_context(native_context) + .expect("Failed to bootstrap surfman context") + }; + + debug!("Creating GL bindings"); let gl = Gl::gl_fns(gl::ffi_gl::Gl::load_with(|s| { gl_context.get_proc_address(s) as *const _ })); let draw_fbo = gl.gen_framebuffers(1)[0]; let read_fbo = gl.gen_framebuffers(1)[0]; + + debug!("Getting the swap chain"); + let (acks, ackr) = crossbeam_channel::bounded(1); + let _ = self.sender.send(ServoWebSrcMsg::GetSwapChain(acks)); + let swap_chain = ackr.recv().expect("Failed to get swap chain"); + ServoWebSrcGfx { device, context, + swap_chain, gl, read_fbo, draw_fbo, } }); + gfx.device + .make_context_current(&gfx.context) + .expect("Failed to make surfman context current"); debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR); // Save the current GL state + debug!("Saving the GL context"); let mut bound_fbos = [0, 0]; unsafe { gfx.gl @@ -751,17 +887,18 @@ impl ServoWebSrc { gfx.gl.framebuffer_texture_2d( gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, - gl::TEXTURE_2D, + draw_texture_target, draw_texture_id, 0, ); debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR); - gfx.gl.clear_color(0.3, 0.2, 0.1, 1.0); + gfx.gl.clear_color(0.0, 0.0, 0.0, 1.0); gfx.gl.clear(gl::COLOR_BUFFER_BIT); debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR); - if let Some(surface) = self.swap_chain.take_surface() { + if let Some(surface) = gfx.swap_chain.take_surface() { + debug!("Rendering surface"); let surface_size = Size2D::from_untyped(gfx.device.surface_info(&surface).size); if size != surface_size { // If we're being asked to fill frames that are a different size than servo is providing, @@ -769,13 +906,23 @@ impl ServoWebSrc { let _ = self.sender.send(ServoWebSrcMsg::Resize(size)); } + if size.width <= 0 || size.height <= 0 { + info!("Surface is zero-sized"); + gfx.swap_chain.recycle_surface(surface); + return; + } + let surface_texture = gfx .device .create_surface_texture(&mut gfx.context, surface) .unwrap(); - let read_texture_id = surface_texture.gl_texture(); + let read_texture_id = gfx.device.surface_texture_object(&surface_texture); let read_texture_target = gfx.device.surface_gl_texture_target(); + debug!( + "Filling with {}/{} {}", + read_texture_id, read_texture_target, surface_size + ); gfx.gl.bind_framebuffer(gl::READ_FRAMEBUFFER, gfx.read_fbo); gfx.gl.framebuffer_texture_2d( gl::READ_FRAMEBUFFER, @@ -794,26 +941,18 @@ impl ServoWebSrc { ); debug_assert_eq!( ( - gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER), + gfx.gl.check_framebuffer_status(gl::READ_FRAMEBUFFER), + gfx.gl.check_framebuffer_status(gl::DRAW_FRAMEBUFFER), gfx.gl.get_error() ), - (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR) - ); - - gfx.gl.clear_color(0.3, 0.7, 0.3, 0.0); - gfx.gl.clear(gl::COLOR_BUFFER_BIT); - debug_assert_eq!( ( - gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER), - gfx.gl.get_error() - ), - (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR) + gl::FRAMEBUFFER_COMPLETE, + gl::FRAMEBUFFER_COMPLETE, + gl::NO_ERROR + ) ); - debug!( - "Filling with {}/{} {}", - read_texture_id, read_texture_target, surface_size - ); + debug!("Blitting"); gfx.gl.blit_framebuffer( 0, 0, @@ -838,7 +977,7 @@ impl ServoWebSrc { .device .destroy_surface_texture(&mut gfx.context, surface_texture) .unwrap(); - self.swap_chain.recycle_surface(surface); + gfx.swap_chain.recycle_surface(surface); } else { debug!("Failed to get current surface"); } @@ -851,11 +990,8 @@ impl ServoWebSrc { debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR); }); - gl_context.activate(false).map_err(|_| { - gst_element_error!(src, CoreError::Failed, ["Failed to deactivate GL context"]); - FlowError::Error - })?; - Ok(()) } } + +// TODO: Implement that trait for more platforms diff --git a/servo-tidy.toml b/servo-tidy.toml index 3996a058c74..6482c47eeb7 100644 --- a/servo-tidy.toml +++ b/servo-tidy.toml @@ -44,6 +44,11 @@ packages = [ # https://github.com/servo/servo/issues/24421 "proc-macro2", "quote", + + # These can be removed once servo is updated to surfman 0.2 + "surfman", + "surfman-chains", + "syn", "smallvec", "unicode-xid", |