diff --git a/Cargo.lock b/Cargo.lock index 51dbf819506..ce8230924cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,6 +410,7 @@ dependencies = [ "servo_config 0.0.1", "webrender 0.60.0 (git+https://github.com/jdm/webrender?branch=servo-hl)", "webrender_api 0.60.0 (git+https://github.com/jdm/webrender?branch=servo-hl)", + "webrender_traits 0.0.1", ] [[package]] @@ -610,6 +611,7 @@ dependencies = [ "pixels 0.0.1", "profile_traits 0.0.1", "script_traits 0.0.1", + "servo-media 0.1.0 (git+https://github.com/servo/media)", "servo_geometry 0.0.1", "servo_url 0.0.1", "style_traits 0.0.1", @@ -646,6 +648,7 @@ dependencies = [ "keyboard-types 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "layout_traits 0.0.1", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "media 0.0.1", "metrics 0.0.1", "msg 0.0.1", "net 0.0.1", @@ -2545,6 +2548,7 @@ dependencies = [ "layout_thread_2013 0.0.1", "layout_thread_2020 0.0.1", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "media 0.0.1", "mozangle 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net 0.0.1", @@ -2565,6 +2569,7 @@ dependencies = [ "webdriver_server 0.0.1", "webrender 0.60.0 (git+https://github.com/jdm/webrender?branch=servo-hl)", "webrender_api 0.60.0 (git+https://github.com/jdm/webrender?branch=servo-hl)", + "webrender_traits 0.0.1", "webvr 0.0.1", "webvr_traits 0.0.1", "webxr-api 0.0.1 (git+https://github.com/servo/webxr)", @@ -2737,6 +2742,23 @@ name = "matches" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "media" +version = "0.0.1" +dependencies = [ + "euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ipc-channel 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "servo-media 0.1.0 (git+https://github.com/servo/media)", + "servo_config 0.0.1", + "webrender 0.60.0 (git+https://github.com/jdm/webrender?branch=servo-hl)", + "webrender_api 0.60.0 (git+https://github.com/jdm/webrender?branch=servo-hl)", + "webrender_traits 0.0.1", +] + [[package]] name = "memchr" version = "2.2.0" @@ -3831,6 +3853,7 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "malloc_size_of 0.0.1", "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "media 0.0.1", "metrics 0.0.1", "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3956,6 +3979,7 @@ dependencies = [ "libc 0.2.53 (registry+https://github.com/rust-lang/crates.io-index)", "malloc_size_of 0.0.1", "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "media 0.0.1", "msg 0.0.1", "net_traits 0.0.1", "pixels 0.0.1", @@ -4062,6 +4086,7 @@ dependencies = [ "osmesa-src 0.1.0 (git+https://github.com/servo/osmesa-src)", "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rust-webvr 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "servo-media 0.1.0 (git+https://github.com/servo/media)", "sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "tinyfiledialogs 3.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "webxr 0.0.1 (git+https://github.com/servo/webxr)", @@ -4109,7 +4134,7 @@ dependencies = [ [[package]] name = "servo-media" version = "0.1.0" -source = "git+https://github.com/servo/media#2dabf1ab7e3b6d3b6764eebdf8855431367752c4" +source = "git+https://github.com/servo/media#c145e2097b027ca82ac767f4820693868a408bb5" dependencies = [ "servo-media-audio 0.1.0 (git+https://github.com/servo/media)", "servo-media-player 0.1.0 (git+https://github.com/servo/media)", @@ -4120,7 +4145,7 @@ dependencies = [ [[package]] name = "servo-media-audio" version = "0.1.0" -source = "git+https://github.com/servo/media#2dabf1ab7e3b6d3b6764eebdf8855431367752c4" +source = "git+https://github.com/servo/media#c145e2097b027ca82ac767f4820693868a408bb5" dependencies = [ "boxfnonce 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "byte-slice-cast 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4136,7 +4161,7 @@ dependencies = [ [[package]] name = "servo-media-dummy" version = "0.1.0" -source = "git+https://github.com/servo/media#2dabf1ab7e3b6d3b6764eebdf8855431367752c4" +source = "git+https://github.com/servo/media#c145e2097b027ca82ac767f4820693868a408bb5" dependencies = [ "boxfnonce 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4150,7 +4175,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer" version = "0.1.0" -source = "git+https://github.com/servo/media#2dabf1ab7e3b6d3b6764eebdf8855431367752c4" +source = "git+https://github.com/servo/media#c145e2097b027ca82ac767f4820693868a408bb5" dependencies = [ "boxfnonce 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "byte-slice-cast 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4184,7 +4209,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer-render" version = "0.1.0" -source = "git+https://github.com/servo/media#2dabf1ab7e3b6d3b6764eebdf8855431367752c4" +source = "git+https://github.com/servo/media#c145e2097b027ca82ac767f4820693868a408bb5" dependencies = [ "gstreamer 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-video 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4194,7 +4219,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer-render-unix" version = "0.1.0" -source = "git+https://github.com/servo/media#2dabf1ab7e3b6d3b6764eebdf8855431367752c4" +source = "git+https://github.com/servo/media#c145e2097b027ca82ac767f4820693868a408bb5" dependencies = [ "glib 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4207,7 +4232,7 @@ dependencies = [ [[package]] name = "servo-media-player" version = "0.1.0" -source = "git+https://github.com/servo/media#2dabf1ab7e3b6d3b6764eebdf8855431367752c4" +source = "git+https://github.com/servo/media#c145e2097b027ca82ac767f4820693868a408bb5" dependencies = [ "ipc-channel 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4218,7 +4243,7 @@ dependencies = [ [[package]] name = "servo-media-streams" version = "0.1.0" -source = "git+https://github.com/servo/media#2dabf1ab7e3b6d3b6764eebdf8855431367752c4" +source = "git+https://github.com/servo/media#c145e2097b027ca82ac767f4820693868a408bb5" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4227,7 +4252,7 @@ dependencies = [ [[package]] name = "servo-media-webrtc" version = "0.1.0" -source = "git+https://github.com/servo/media#2dabf1ab7e3b6d3b6764eebdf8855431367752c4" +source = "git+https://github.com/servo/media#c145e2097b027ca82ac767f4820693868a408bb5" dependencies = [ "boxfnonce 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4326,7 +4351,7 @@ dependencies = [ [[package]] name = "servo_media_derive" version = "0.1.0" -source = "git+https://github.com/servo/media#2dabf1ab7e3b6d3b6764eebdf8855431367752c4" +source = "git+https://github.com/servo/media#c145e2097b027ca82ac767f4820693868a408bb5" dependencies = [ "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4430,6 +4455,7 @@ dependencies = [ "libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "libservo 0.0.1", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "servo-media 0.1.0 (git+https://github.com/servo/media)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5405,6 +5431,15 @@ dependencies = [ "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "webrender_traits" +version = "0.0.1" +dependencies = [ + "euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.60.0 (git+https://github.com/jdm/webrender?branch=servo-hl)", + "webrender_api 0.60.0 (git+https://github.com/jdm/webrender?branch=servo-hl)", +] + [[package]] name = "webvr" version = "0.0.1" diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 906fa84b434..8211619ca91 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -37,3 +37,4 @@ serde_bytes = "0.10" servo_config = {path = "../config"} webrender = {git = "https://github.com/servo/webrender"} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} +webrender_traits = {path = "../webrender_traits"} diff --git a/components/canvas/webgl_mode/inprocess.rs b/components/canvas/webgl_mode/inprocess.rs index b93b2fb0a19..d0203f7498b 100644 --- a/components/canvas/webgl_mode/inprocess.rs +++ b/components/canvas/webgl_mode/inprocess.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::gl_context::GLContextFactory; -use crate::webgl_thread::{WebGLExternalImageApi, WebGLExternalImageHandler, WebGLThread}; +use crate::webgl_thread::WebGLThread; use canvas_traits::webgl::webgl_channel; use canvas_traits::webgl::DOMToTextureCommand; use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver}; @@ -13,6 +13,8 @@ use fnv::FnvHashMap; use gleam::gl; use servo_config::pref; use std::rc::Rc; +use std::sync::{Arc, Mutex}; +use webrender_traits::{WebrenderExternalImageApi, WebrenderExternalImageRegistry}; /// WebGL Threading API entry point that lives in the constellation. pub struct WebGLThreads(WebGLSender); @@ -24,9 +26,10 @@ impl WebGLThreads { webrender_gl: Rc, webrender_api_sender: webrender_api::RenderApiSender, webvr_compositor: Option>, + external_images: Arc>, ) -> ( WebGLThreads, - Box, + Box, Option>, ) { // This implementation creates a single `WebGLThread` for all the pipelines. @@ -34,6 +37,7 @@ impl WebGLThreads { gl_factory, webrender_api_sender, webvr_compositor.map(|c| WebVRRenderWrapper(c)), + external_images, ); let output_handler = if pref!(dom.webgl.dom_to_texture.enabled) { Some(Box::new(OutputHandler::new( @@ -43,8 +47,7 @@ impl WebGLThreads { } else { None }; - let external = - WebGLExternalImageHandler::new(WebGLExternalImages::new(webrender_gl, channel.clone())); + let external = WebGLExternalImages::new(webrender_gl, channel.clone()); ( WebGLThreads(channel), Box::new(external), @@ -87,12 +90,15 @@ impl WebGLExternalImages { } } -impl WebGLExternalImageApi for WebGLExternalImages { - fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D) { +impl WebrenderExternalImageApi for WebGLExternalImages { + fn lock(&mut self, id: u64) -> (u32, Size2D) { // WebGL Thread has it's own GL command queue that we need to synchronize with the WR GL command queue. // The WebGLMsg::Lock message inserts a fence in the WebGL command queue. self.webgl_channel - .send(WebGLMsg::Lock(ctx_id, self.lock_channel.0.clone())) + .send(WebGLMsg::Lock( + WebGLContextId(id as usize), + self.lock_channel.0.clone(), + )) .unwrap(); let (image_id, size, gl_sync) = self.lock_channel.1.recv().unwrap(); // The next glWaitSync call is run on the WR thread and it's used to synchronize the two @@ -103,8 +109,10 @@ impl WebGLExternalImageApi for WebGLExternalImages { (image_id, size) } - fn unlock(&mut self, ctx_id: WebGLContextId) { - self.webgl_channel.send(WebGLMsg::Unlock(ctx_id)).unwrap(); + fn unlock(&mut self, id: u64) { + self.webgl_channel + .send(WebGLMsg::Unlock(WebGLContextId(id as usize))) + .unwrap(); } } diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs index cec8cc929d6..4b2e59dd216 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -12,7 +12,9 @@ use half::f16; use offscreen_gl_context::{DrawBuffer, GLContext, NativeGLContextMethods}; use pixels::{self, PixelFormat}; use std::borrow::Cow; +use std::sync::{Arc, Mutex}; use std::thread; +use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType}; /// WebGL Threading API entry point that lives in the constellation. /// It allows to get a WebGLThread handle for each script pipeline. @@ -58,12 +60,13 @@ pub struct WebGLThread { cached_context_info: FnvHashMap, /// Current bound context. bound_context_id: Option, - /// Id generator for new WebGLContexts. - next_webgl_id: usize, /// Handler user to send WebVR commands. webvr_compositor: Option, /// Texture ids and sizes used in DOM to texture outputs. dom_outputs: FnvHashMap, + /// List of registered webrender external images. + /// We use it to get an unique ID for new WebGLContexts. + external_images: Arc>, } impl WebGLThread { @@ -71,6 +74,7 @@ impl WebGLThread { gl_factory: GLContextFactory, webrender_api_sender: webrender_api::RenderApiSender, webvr_compositor: Option, + external_images: Arc>, ) -> Self { WebGLThread { gl_factory, @@ -78,9 +82,9 @@ impl WebGLThread { contexts: Default::default(), cached_context_info: Default::default(), bound_context_id: None, - next_webgl_id: 0, webvr_compositor, dom_outputs: Default::default(), + external_images, } } @@ -90,14 +94,19 @@ impl WebGLThread { gl_factory: GLContextFactory, webrender_api_sender: webrender_api::RenderApiSender, webvr_compositor: Option, + external_images: Arc>, ) -> WebGLSender { let (sender, receiver) = webgl_channel::().unwrap(); let result = sender.clone(); thread::Builder::new() .name("WebGLThread".to_owned()) .spawn(move || { - let mut renderer = - WebGLThread::new(gl_factory, webrender_api_sender, webvr_compositor); + let mut renderer = WebGLThread::new( + gl_factory, + webrender_api_sender, + webvr_compositor, + external_images, + ); let webgl_chan = WebGLChan(sender); loop { let msg = receiver.recv().unwrap(); @@ -287,9 +296,14 @@ impl WebGLThread { }) .map_err(|msg: &str| msg.to_owned())?; - let id = WebGLContextId(self.next_webgl_id); + let id = WebGLContextId( + self.external_images + .lock() + .unwrap() + .next_id(WebrenderImageHandlerType::WebGL) + .0 as usize, + ); let (size, texture_id, limits) = ctx.get_info(); - self.next_webgl_id += 1; self.contexts.insert( id, GLContextData { @@ -730,53 +744,6 @@ struct WebGLContextInfo { render_state: ContextRenderState, } -/// This trait is used as a bridge between the `WebGLThreads` implementation and -/// the WR ExternalImageHandler API implemented in the `WebGLExternalImageHandler` struct. -/// `WebGLExternalImageHandler` takes care of type conversions between WR and WebGL info (e.g keys, uvs). -/// It uses this trait to notify lock/unlock messages and get the required info that WR needs. -/// `WebGLThreads` receives lock/unlock message notifications and takes care of sending -/// the unlock/lock messages to the appropiate `WebGLThread`. -pub trait WebGLExternalImageApi { - fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D); - fn unlock(&mut self, ctx_id: WebGLContextId); -} - -/// WebRender External Image Handler implementation -pub struct WebGLExternalImageHandler { - handler: T, -} - -impl WebGLExternalImageHandler { - pub fn new(handler: T) -> Self { - Self { handler: handler } - } -} - -impl webrender::ExternalImageHandler for WebGLExternalImageHandler { - /// Lock the external image. Then, WR could start to read the image content. - /// The WR client should not change the image content until the unlock() call. - fn lock( - &mut self, - key: webrender_api::ExternalImageId, - _channel_index: u8, - _rendering: webrender_api::ImageRendering, - ) -> webrender::ExternalImage { - let ctx_id = WebGLContextId(key.0 as _); - let (texture_id, size) = self.handler.lock(ctx_id); - - webrender::ExternalImage { - uv: webrender_api::TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0), - source: webrender::ExternalImageSource::NativeTexture(texture_id), - } - } - /// Unlock the external image. The WR should not read the image content - /// after this call. - fn unlock(&mut self, key: webrender_api::ExternalImageId, _channel_index: u8) { - let ctx_id = WebGLContextId(key.0 as _); - self.handler.unlock(ctx_id); - } -} - /// Data about the linked DOM<->WebGLTexture elements. struct DOMToTextureData { context_id: WebGLContextId, diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index 6ac076e3362..2d5fa600376 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -33,6 +33,7 @@ pixels = {path = "../pixels", optional = true} profile_traits = {path = "../profile_traits"} script_traits = {path = "../script_traits"} servo_geometry = {path = "../geometry"} +servo-media = {git = "https://github.com/servo/media"} servo_url = {path = "../url"} style_traits = {path = "../style_traits"} time = "0.1.17" diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 9db0286bd5a..0cbb51249c6 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -12,6 +12,7 @@ use keyboard_types::KeyboardEvent; use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId, TraversalDirection}; use script_traits::{MouseButton, TouchEventType, TouchId, WheelDelta}; use servo_geometry::DeviceIndependentPixel; +use servo_media::player::context::{GlApi, GlContext, NativeDisplay}; use servo_url::ServoUrl; use std::fmt::{Debug, Error, Formatter}; #[cfg(feature = "gl")] @@ -157,6 +158,12 @@ pub trait WindowMethods { /// will want to avoid blocking on UI events, and just /// run the event loop at the vsync interval. fn set_animation_state(&self, _state: AnimationState); + /// Get the GL context + fn get_gl_context(&self) -> GlContext; + /// Get the native display + fn get_native_display(&self) -> NativeDisplay; + /// Get the GL api + fn get_gl_api(&self) -> GlApi; } pub trait EmbedderMethods { diff --git a/components/config/prefs.rs b/components/config/prefs.rs index 0c6e394ffaa..1bd1a745799 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -446,6 +446,9 @@ mod gen { } }, media: { + glvideo: { + enabled: bool, + }, testing: { enabled: bool, } diff --git a/components/constellation/Cargo.toml b/components/constellation/Cargo.toml index f91396343d8..f5270aa2d90 100644 --- a/components/constellation/Cargo.toml +++ b/components/constellation/Cargo.toml @@ -35,6 +35,7 @@ ipc-channel = "0.11" layout_traits = {path = "../layout_traits"} keyboard-types = "0.4.3" log = "0.4" +media = {path = "../media"} metrics = {path = "../metrics"} msg = {path = "../msg"} net = {path = "../net"} diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 3fd21e735e4..77fd77ab307 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -123,6 +123,7 @@ use keyboard_types::webdriver::Event as WebDriverInputEvent; use keyboard_types::KeyboardEvent; use layout_traits::LayoutThreadFactory; use log::{Level, LevelFilter, Log, Metadata, Record}; +use media::{GLPlayerThreads, WindowGLContext}; use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg}; use msg::constellation_msg::{ BrowsingContextGroupId, BrowsingContextId, HistoryStateId, PipelineId, @@ -409,6 +410,12 @@ pub struct Constellation { /// Like --disable-text-aa, this is useful for reftests where pixel perfect /// results are required. enable_canvas_antialiasing: bool, + + /// Entry point to create and get channels to a GLPlayerThread. + glplayer_threads: Option, + + /// Application window's GL Context for Media player + player_context: WindowGLContext, } /// State needed to construct a constellation. @@ -457,6 +464,11 @@ pub struct InitialConstellationState { /// The XR device registry pub webxr_registry: webxr_api::Registry, + + pub glplayer_threads: Option, + + /// Application window's GL Context for Media player + pub player_context: WindowGLContext, } /// Data needed for webdriver @@ -753,6 +765,8 @@ where is_running_problem_test, hard_fail, enable_canvas_antialiasing, + glplayer_threads: state.glplayer_threads, + player_context: state.player_context, }; constellation.run(); @@ -994,6 +1008,7 @@ where .map(|threads| threads.pipeline()), webvr_chan: self.webvr_chan.clone(), webxr_registry: self.webxr_registry.clone(), + player_context: self.player_context.clone(), }); let pipeline = match result { @@ -1795,6 +1810,13 @@ where } } + debug!("Exiting GLPlayer thread."); + if let Some(glplayer_threads) = self.glplayer_threads.as_ref() { + if let Err(e) = glplayer_threads.exit() { + warn!("Exit GLPlayer Thread failed ({})", e); + } + } + debug!("Exiting timer scheduler."); if let Err(e) = self.scheduler_chan.send(TimerSchedulerMsg::Exit) { warn!("Exit timer scheduler failed ({})", e); diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 9b1f0e7b336..f3acb29ad14 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -17,6 +17,7 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use ipc_channel::Error; use layout_traits::LayoutThreadFactory; +use media::WindowGLContext; use metrics::PaintTimeMetrics; use msg::constellation_msg::TopLevelBrowsingContextId; use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg}; @@ -191,6 +192,9 @@ pub struct InitialPipelineState { /// The XR device registry pub webxr_registry: webxr_api::Registry, + + /// Application window's GL Context for Media player + pub player_context: WindowGLContext, } pub struct NewPipeline { @@ -309,6 +313,7 @@ impl Pipeline { webgl_chan: state.webgl_chan, webvr_chan: state.webvr_chan, webxr_registry: state.webxr_registry, + player_context: state.player_context, }; // Spawn the child process. @@ -515,6 +520,7 @@ pub struct UnprivilegedPipelineContent { webgl_chan: Option, webvr_chan: Option>, webxr_registry: webxr_api::Registry, + player_context: WindowGLContext, } impl UnprivilegedPipelineContent { @@ -563,6 +569,7 @@ impl UnprivilegedPipelineContent { webrender_document: self.webrender_document, webrender_api_sender: self.webrender_api_sender.clone(), layout_is_busy: layout_thread_busy_flag.clone(), + player_context: self.player_context.clone(), }, self.load_data.clone(), self.opts.profile_script_events, diff --git a/components/media/Cargo.toml b/components/media/Cargo.toml new file mode 100644 index 00000000000..254b4246f7e --- /dev/null +++ b/components/media/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "media" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +edition = "2018" +publish = false + +[lib] +name = "media" +path = "lib.rs" + +[dependencies] +euclid = "0.19" +fnv = "1.0" +ipc-channel = "0.11" +lazy_static = "1" +log = "0.4" +serde = "1.0" +servo_config = {path = "../config"} +servo-media = {git = "https://github.com/servo/media"} +webrender = {git = "https://github.com/servo/webrender"} +webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} +webrender_traits = {path = "../webrender_traits"} diff --git a/components/media/lib.rs b/components/media/lib.rs new file mode 100644 index 00000000000..7c339693da2 --- /dev/null +++ b/components/media/lib.rs @@ -0,0 +1,169 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#![deny(unsafe_code)] + +#[macro_use] +extern crate lazy_static; +#[macro_use] +extern crate log; +#[macro_use] +extern crate serde; + +mod media_channel; +mod media_thread; + +pub use crate::media_channel::glplayer_channel; +use crate::media_channel::{GLPlayerChan, GLPlayerPipeline, GLPlayerReceiver, GLPlayerSender}; +use crate::media_thread::GLPlayerThread; +use euclid::Size2D; +use servo_media::player::context::{GlApi, GlContext, NativeDisplay, PlayerGLContext}; +use std::sync::{Arc, Mutex}; +use webrender_traits::{WebrenderExternalImageApi, WebrenderExternalImageRegistry}; + +/// These are the messages that the GLPlayer thread will forward to +/// the video player which lives in htmlmediaelement +#[derive(Debug, Deserialize, Serialize)] +pub enum GLPlayerMsgForward { + PlayerId(u64), + Lock(GLPlayerSender<(u32, Size2D, usize)>), + Unlock(), +} + +/// GLPlayer thread Message API +/// +/// These are the messages that the thread will receive from the +/// constellation, the webrender::ExternalImageHandle demultiplexor +/// implementation, or a htmlmediaelement +#[derive(Debug, Deserialize, Serialize)] +pub enum GLPlayerMsg { + /// Registers an instantiated player in DOM + RegisterPlayer(GLPlayerSender), + /// Unregisters a player's ID + UnregisterPlayer(u64), + /// Locks a specific texture from a player. Lock messages are used + /// for a correct synchronization with WebRender external image + /// API. + /// + /// WR locks a external texture when it wants to use the shared + /// texture contents. + /// + /// The WR client should not change the shared texture content + /// until the Unlock call. + /// + /// Currently OpenGL Sync Objects are used to implement the + /// synchronization mechanism. + Lock(u64, GLPlayerSender<(u32, Size2D, usize)>), + /// Unlocks a specific texture from a player. Unlock messages are + /// used for a correct synchronization with WebRender external + /// image API. + /// + /// The WR unlocks a context when it finished reading the shared + /// texture contents. + /// + /// Unlock messages are always sent after a Lock message. + Unlock(u64), + /// Frees all resources and closes the thread. + Exit, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct WindowGLContext { + /// Application's GL Context + pub gl_context: GlContext, + /// Application's GL Api + pub gl_api: GlApi, + /// Application's native display + pub native_display: NativeDisplay, + /// A channel to the GLPlayer thread. + pub glplayer_chan: Option, +} + +impl PlayerGLContext for WindowGLContext { + fn get_gl_context(&self) -> GlContext { + self.gl_context.clone() + } + + fn get_native_display(&self) -> NativeDisplay { + self.native_display.clone() + } + + fn get_gl_api(&self) -> GlApi { + self.gl_api.clone() + } +} + +/// GLPlayer Threading API entry point that lives in the constellation. +pub struct GLPlayerThreads(GLPlayerSender); + +impl GLPlayerThreads { + pub fn new( + external_images: Arc>, + ) -> (GLPlayerThreads, Box) { + let channel = GLPlayerThread::start(external_images); + let external = GLPlayerExternalImages::new(channel.clone()); + (GLPlayerThreads(channel), Box::new(external)) + } + + /// Gets the GLPlayerThread handle for each script pipeline. + pub fn pipeline(&self) -> GLPlayerPipeline { + // This mode creates a single thread, so the existing + // GLPlayerChan is just cloned. + GLPlayerPipeline(GLPlayerChan(self.0.clone())) + } + + /// Sends an exit message to close the GLPlayerThreads + pub fn exit(&self) -> Result<(), &'static str> { + self.0 + .send(GLPlayerMsg::Exit) + .map_err(|_| "Failed to send Exit message") + } +} + +/// Bridge between the webrender::ExternalImage callbacks and the +/// GLPlayerThreads. +struct GLPlayerExternalImages { + // @FIXME(victor): this should be added when GstGLSyncMeta is + // added + //webrender_gl: Rc, + glplayer_channel: GLPlayerSender, + // Used to avoid creating a new channel on each received WebRender + // request. + lock_channel: ( + GLPlayerSender<(u32, Size2D, usize)>, + GLPlayerReceiver<(u32, Size2D, usize)>, + ), +} + +impl GLPlayerExternalImages { + fn new(channel: GLPlayerSender) -> Self { + Self { + glplayer_channel: channel, + lock_channel: glplayer_channel().unwrap(), + } + } +} + +impl WebrenderExternalImageApi for GLPlayerExternalImages { + fn lock(&mut self, id: u64) -> (u32, Size2D) { + // The GLPlayerMsgForward::Lock message inserts a fence in the + // GLPlayer command queue. + self.glplayer_channel + .send(GLPlayerMsg::Lock(id, self.lock_channel.0.clone())) + .unwrap(); + let (image_id, size, _gl_sync) = self.lock_channel.1.recv().unwrap(); + // The next glWaitSync call is run on the WR thread and it's + // used to synchronize the two flows of OpenGL commands in + // order to avoid WR using a semi-ready GLPlayer texture. + // glWaitSync doesn't block WR thread, it affects only + // internal OpenGL subsystem. + //self.webrender_gl + // .wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED); + (image_id, size) + } + + fn unlock(&mut self, id: u64) { + self.glplayer_channel.send(GLPlayerMsg::Unlock(id)).unwrap(); + } +} diff --git a/components/media/media_channel/ipc.rs b/components/media/media_channel/ipc.rs new file mode 100644 index 00000000000..67548a8ddce --- /dev/null +++ b/components/media/media_channel/ipc.rs @@ -0,0 +1,14 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use serde::{Deserialize, Serialize}; +use std::io; + +pub type GLPlayerSender = ipc_channel::ipc::IpcSender; +pub type GLPlayerReceiver = ipc_channel::ipc::IpcReceiver; + +pub fn glplayer_channel Deserialize<'de>>( +) -> Result<(GLPlayerSender, GLPlayerReceiver), io::Error> { + ipc_channel::ipc::channel() +} diff --git a/components/media/media_channel/mod.rs b/components/media/media_channel/mod.rs new file mode 100644 index 00000000000..5616a8479fd --- /dev/null +++ b/components/media/media_channel/mod.rs @@ -0,0 +1,114 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! Enum wrappers to be able to select different channel implementations at runtime. + +mod ipc; +mod mpsc; + +use crate::GLPlayerMsg; +use serde::{Deserialize, Serialize}; +use servo_config::opts; +use std::fmt; + +lazy_static! { + static ref IS_MULTIPROCESS: bool = { opts::multiprocess() }; +} + +#[derive(Deserialize, Serialize)] +pub enum GLPlayerSender { + Ipc(ipc::GLPlayerSender), + Mpsc(mpsc::GLPlayerSender), +} + +impl Clone for GLPlayerSender +where + T: Serialize, +{ + fn clone(&self) -> Self { + match *self { + GLPlayerSender::Ipc(ref chan) => GLPlayerSender::Ipc(chan.clone()), + GLPlayerSender::Mpsc(ref chan) => GLPlayerSender::Mpsc(chan.clone()), + } + } +} + +impl fmt::Debug for GLPlayerSender { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "GLPlayerSender(..)") + } +} + +impl GLPlayerSender { + #[inline] + pub fn send(&self, msg: T) -> GLPlayerSendResult { + match *self { + GLPlayerSender::Ipc(ref sender) => sender.send(msg).map_err(|_| ()), + GLPlayerSender::Mpsc(ref sender) => sender.send(msg).map_err(|_| ()), + } + } +} + +pub type GLPlayerSendResult = Result<(), ()>; + +pub enum GLPlayerReceiver +where + T: for<'de> Deserialize<'de> + Serialize, +{ + Ipc(ipc::GLPlayerReceiver), + Mpsc(mpsc::GLPlayerReceiver), +} + +impl GLPlayerReceiver +where + T: for<'de> Deserialize<'de> + Serialize, +{ + pub fn recv(&self) -> Result { + match *self { + GLPlayerReceiver::Ipc(ref receiver) => receiver.recv().map_err(|_| ()), + GLPlayerReceiver::Mpsc(ref receiver) => receiver.recv().map_err(|_| ()), + } + } + + pub fn to_opaque(self) -> ipc_channel::ipc::OpaqueIpcReceiver { + match self { + GLPlayerReceiver::Ipc(receiver) => receiver.to_opaque(), + _ => unreachable!(), + } + } +} + +pub fn glplayer_channel() -> Result<(GLPlayerSender, GLPlayerReceiver), ()> +where + T: for<'de> Deserialize<'de> + Serialize, +{ + // Let's use Ipc until we move the Player instance into GPlayerThread + if true { + ipc::glplayer_channel() + .map(|(tx, rx)| (GLPlayerSender::Ipc(tx), GLPlayerReceiver::Ipc(rx))) + .map_err(|_| ()) + } else { + mpsc::glplayer_channel() + .map(|(tx, rx)| (GLPlayerSender::Mpsc(tx), GLPlayerReceiver::Mpsc(rx))) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct GLPlayerChan(pub GLPlayerSender); + +impl GLPlayerChan { + #[inline] + pub fn send(&self, msg: GLPlayerMsg) -> GLPlayerSendResult { + self.0.send(msg) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct GLPlayerPipeline(pub GLPlayerChan); + +impl GLPlayerPipeline { + pub fn channel(&self) -> GLPlayerChan { + self.0.clone() + } +} diff --git a/components/media/media_channel/mpsc.rs b/components/media/media_channel/mpsc.rs new file mode 100644 index 00000000000..7d6a396185a --- /dev/null +++ b/components/media/media_channel/mpsc.rs @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use serde::{Deserialize, Serialize}; +use serde::{Deserializer, Serializer}; +use std::sync::mpsc; + +#[macro_use] +macro_rules! unreachable_serializable { + ($name:ident) => { + impl Serialize for $name { + fn serialize(&self, _: S) -> Result { + unreachable!(); + } + } + + impl<'a, T> Deserialize<'a> for $name { + fn deserialize(_: D) -> Result<$name, D::Error> + where + D: Deserializer<'a>, + { + unreachable!(); + } + } + }; +} + +pub struct GLPlayerSender(mpsc::Sender); +pub struct GLPlayerReceiver(mpsc::Receiver); + +impl Clone for GLPlayerSender { + fn clone(&self) -> Self { + GLPlayerSender(self.0.clone()) + } +} + +impl GLPlayerSender { + #[inline] + pub fn send(&self, data: T) -> Result<(), mpsc::SendError> { + self.0.send(data) + } +} + +impl GLPlayerReceiver { + #[inline] + pub fn recv(&self) -> Result { + self.0.recv() + } +} + +pub fn glplayer_channel() -> Result<(GLPlayerSender, GLPlayerReceiver), ()> { + let (sender, receiver) = mpsc::channel(); + Ok((GLPlayerSender(sender), GLPlayerReceiver(receiver))) +} + +unreachable_serializable!(GLPlayerReceiver); +unreachable_serializable!(GLPlayerSender); diff --git a/components/media/media_thread.rs b/components/media/media_thread.rs new file mode 100644 index 00000000000..9dc3bf625f1 --- /dev/null +++ b/components/media/media_thread.rs @@ -0,0 +1,92 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::media_channel::{glplayer_channel, GLPlayerSender}; +/// GL player threading API entry point that lives in the +/// constellation. +use crate::{GLPlayerMsg, GLPlayerMsgForward}; +use fnv::FnvHashMap; +use std::sync::{Arc, Mutex}; +use std::thread; +use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType}; + +/// A GLPlayerThread manages the life cycle and message demultiplexing of +/// a set of video players with GL render. +pub struct GLPlayerThread { + /// Map of live players. + players: FnvHashMap>, + /// List of registered webrender external images. + /// We use it to get an unique ID for new players. + external_images: Arc>, +} + +impl GLPlayerThread { + pub fn new(external_images: Arc>) -> Self { + GLPlayerThread { + players: Default::default(), + external_images, + } + } + + pub fn start( + external_images: Arc>, + ) -> GLPlayerSender { + let (sender, receiver) = glplayer_channel::().unwrap(); + thread::Builder::new() + .name("GLPlayerThread".to_owned()) + .spawn(move || { + let mut renderer = GLPlayerThread::new(external_images); + loop { + let msg = receiver.recv().unwrap(); + let exit = renderer.handle_msg(msg); + if exit { + return; + } + } + }) + .expect("Thread spawning failed"); + + sender + } + + /// Handles a generic GLPlayerMsg message + #[inline] + fn handle_msg(&mut self, msg: GLPlayerMsg) -> bool { + trace!("processing {:?}", msg); + match msg { + GLPlayerMsg::RegisterPlayer(sender) => { + let id = self + .external_images + .lock() + .unwrap() + .next_id(WebrenderImageHandlerType::Media) + .0; + self.players.insert(id, sender.clone()); + sender.send(GLPlayerMsgForward::PlayerId(id)).unwrap(); + }, + GLPlayerMsg::UnregisterPlayer(id) => { + self.external_images + .lock() + .unwrap() + .remove(&webrender_api::ExternalImageId(id)); + if self.players.remove(&id).is_none() { + warn!("Tried to remove an unknown player"); + } + }, + GLPlayerMsg::Lock(id, handler_sender) => { + self.players.get(&id).map(|sender| { + sender.send(GLPlayerMsgForward::Lock(handler_sender)).ok(); + }); + }, + GLPlayerMsg::Unlock(id) => { + self.players.get(&id).map(|sender| { + sender.send(GLPlayerMsgForward::Unlock()).ok(); + }); + }, + GLPlayerMsg::Exit => return true, + } + + false + } +} diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index fd270a74fc8..e0f11c85434 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -70,6 +70,7 @@ libc = "0.2" log = "0.4" malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = "0.1" +media = {path = "../media"} metrics = {path = "../metrics"} mitochondria = "1.1.2" mime = "0.3.13" diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 7d5d0b0b093..d9e0cf893b6 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -73,6 +73,7 @@ use js::jsval::JSVal; use js::rust::{GCMethods, Handle, Runtime}; use js::typedarray::TypedArray; use js::typedarray::TypedArrayElement; +use media::WindowGLContext; use metrics::{InteractiveMetrics, InteractiveWindow}; use mime::Mime; use msg::constellation_msg::{ @@ -507,6 +508,7 @@ unsafe_no_jsmanaged_fields!(Rotation3D, Transform2D, Transform3D) unsafe_no_jsmanaged_fields!(Point2D, Vector2D, Rect); unsafe_no_jsmanaged_fields!(Rect, RigidTransform3D); unsafe_no_jsmanaged_fields!(CascadeData); +unsafe_no_jsmanaged_fields!(WindowGLContext); unsafe impl<'a> JSTraceable for &'a str { #[inline] diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 690dd928f45..690692a2481 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -59,11 +59,13 @@ use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingLi use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use dom_struct::dom_struct; +use euclid::Size2D; use headers::{ContentLength, ContentRange, HeaderMapExt}; use html5ever::{LocalName, Prefix}; use http::header::{self, HeaderMap, HeaderValue}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; +use media::{glplayer_channel, GLPlayerMsg, GLPlayerMsgForward}; use net_traits::image::base::Image; use net_traits::image_cache::ImageResponse; use net_traits::request::{CredentialsMode, Destination, Referrer, RequestBuilder, RequestMode}; @@ -71,7 +73,6 @@ use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseLis use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType}; use script_layout_interface::HTMLMediaData; use servo_config::pref; -use servo_media::player::context::{GlContext, NativeDisplay, PlayerGLContext}; use servo_media::player::frame::{Frame, FrameRenderer}; use servo_media::player::{PlaybackState, Player, PlayerError, PlayerEvent, StreamType}; use servo_media::{ServoMedia, SupportsMediaType}; @@ -83,23 +84,72 @@ use std::mem; use std::rc::Rc; use std::sync::{Arc, Mutex}; use time::{self, Duration, Timespec}; +use webrender_api::{ExternalImageData, ExternalImageId, ExternalImageType, TextureTarget}; use webrender_api::{ImageData, ImageDescriptor, ImageFormat, ImageKey, RenderApi}; use webrender_api::{RenderApiSender, Transaction}; +#[derive(PartialEq)] +enum FrameStatus { + Locked, + Unlocked, +} + +struct FrameHolder(FrameStatus, Frame); + +impl FrameHolder { + fn new(frame: Frame) -> FrameHolder { + FrameHolder(FrameStatus::Unlocked, frame) + } + + fn lock(&mut self) { + if self.0 == FrameStatus::Unlocked { + self.0 = FrameStatus::Locked; + }; + } + + fn unlock(&mut self) { + if self.0 == FrameStatus::Locked { + self.0 = FrameStatus::Unlocked; + }; + } + + fn set(&mut self, new_frame: Frame) { + if self.0 == FrameStatus::Unlocked { + self.1 = new_frame + }; + } + + fn get(&self) -> (u32, Size2D, usize) { + if self.0 == FrameStatus::Locked { + ( + self.1.get_texture_id(), + Size2D::new(self.1.get_width(), self.1.get_height()), + 0, + ) + } else { + unreachable!(); + } + } +} + pub struct MediaFrameRenderer { + player_id: Option, api: RenderApi, current_frame: Option<(ImageKey, i32, i32)>, old_frame: Option, very_old_frame: Option, + current_frame_holder: Option, } impl MediaFrameRenderer { fn new(render_api_sender: RenderApiSender) -> Self { Self { + player_id: None, api: render_api_sender.create_api(), current_frame: None, old_frame: None, very_old_frame: None, + current_frame_holder: None, } } @@ -112,6 +162,12 @@ impl MediaFrameRenderer { impl FrameRenderer for MediaFrameRenderer { fn render(&mut self, frame: Frame) { + let mut txn = Transaction::new(); + + if let Some(old_image_key) = mem::replace(&mut self.very_old_frame, self.old_frame.take()) { + txn.delete_image(old_image_key); + } + let descriptor = ImageDescriptor::new( frame.get_width(), frame.get_height(), @@ -120,24 +176,22 @@ impl FrameRenderer for MediaFrameRenderer { false, ); - let mut txn = Transaction::new(); - - let image_data = ImageData::Raw(frame.get_data()); - - if let Some(old_image_key) = mem::replace(&mut self.very_old_frame, self.old_frame.take()) { - txn.delete_image(old_image_key); - } - match self.current_frame { Some((ref image_key, ref mut width, ref mut height)) if *width == frame.get_width() && *height == frame.get_height() => { - txn.update_image( - *image_key, - descriptor, - image_data, - &webrender_api::DirtyRect::All, - ); + if !frame.is_gl_texture() { + txn.update_image( + *image_key, + descriptor, + ImageData::Raw(frame.get_data()), + &webrender_api::DirtyRect::All, + ); + } else if self.player_id.is_some() { + self.current_frame_holder + .get_or_insert_with(|| FrameHolder::new(frame.clone())) + .set(frame); + } if let Some(old_image_key) = self.old_frame.take() { txn.delete_image(old_image_key); @@ -147,31 +201,47 @@ impl FrameRenderer for MediaFrameRenderer { self.old_frame = Some(*image_key); let new_image_key = self.api.generate_image_key(); - txn.add_image(new_image_key, descriptor, image_data, None); + + /* update current_frame */ *image_key = new_image_key; *width = frame.get_width(); *height = frame.get_height(); + + let image_data = if frame.is_gl_texture() && self.player_id.is_some() { + self.current_frame_holder + .get_or_insert_with(|| FrameHolder::new(frame.clone())) + .set(frame); + ImageData::External(ExternalImageData { + id: ExternalImageId(self.player_id.unwrap()), + channel_index: 0, + image_type: ExternalImageType::TextureHandle(TextureTarget::Default), + }) + } else { + ImageData::Raw(frame.get_data()) + }; + txn.add_image(new_image_key, descriptor, image_data, None); }, None => { let image_key = self.api.generate_image_key(); - txn.add_image(image_key, descriptor, image_data, None); self.current_frame = Some((image_key, frame.get_width(), frame.get_height())); + + let image_data = if frame.is_gl_texture() && self.player_id.is_some() { + self.current_frame_holder = Some(FrameHolder::new(frame)); + ImageData::External(ExternalImageData { + id: ExternalImageId(self.player_id.unwrap()), + channel_index: 0, + image_type: ExternalImageType::TextureHandle(TextureTarget::Default), + }) + } else { + ImageData::Raw(frame.get_data()) + }; + txn.add_image(image_key, descriptor, image_data, None); }, } self.api.update_resources(txn.resource_updates); } } -struct PlayerContextDummy(); -impl PlayerGLContext for PlayerContextDummy { - fn get_gl_context(&self) -> GlContext { - return GlContext::Unknown; - } - fn get_native_display(&self) -> NativeDisplay { - return NativeDisplay::Unknown; - } -} - #[must_root] #[derive(JSTraceable, MallocSizeOf)] enum SrcObject { @@ -263,6 +333,8 @@ pub struct HTMLMediaElement { next_timeupdate_event: Cell, /// Latest fetch request context. current_fetch_context: DomRefCell>, + /// Player Id reported the player thread + id: Cell, } /// @@ -324,6 +396,7 @@ impl HTMLMediaElement { text_tracks_list: Default::default(), next_timeupdate_event: Cell::new(time::get_time() + Duration::milliseconds(250)), current_fetch_context: DomRefCell::new(None), + id: Cell::new(0), } } @@ -1222,29 +1295,30 @@ impl HTMLMediaElement { _ => StreamType::Seekable, }; - let (action_sender, action_receiver) = ipc::channel().unwrap(); + let window = window_from_node(self); + let (action_sender, action_receiver) = ipc::channel::().unwrap(); let renderer: Option>> = match self.media_type_id() { HTMLMediaElementTypeId::HTMLAudioElement => None, HTMLMediaElementTypeId::HTMLVideoElement => Some(self.frame_renderer.clone()), }; + let player = ServoMedia::get().unwrap().create_player( stream_type, action_sender, renderer, - Box::new(PlayerContextDummy()), + Box::new(window.get_player_context()), ); *self.player.borrow_mut() = Some(player); let trusted_node = Trusted::new(self); - let window = window_from_node(self); let (task_source, canceller) = window .task_manager() .media_element_task_source_with_canceller(); ROUTER.add_route( action_receiver.to_opaque(), Box::new(move |message| { - let event: PlayerEvent = message.to().unwrap(); + let event = message.to().unwrap(); trace!("Player event {:?}", event); let this = trusted_node.clone(); if let Err(err) = task_source.queue_with_canceller( @@ -1258,6 +1332,73 @@ impl HTMLMediaElement { }), ); + // GLPlayer thread setup + let (player_id, image_receiver) = window + .get_player_context() + .glplayer_chan + .map(|pipeline| { + let (image_sender, image_receiver) = + glplayer_channel::().unwrap(); + pipeline + .channel() + .send(GLPlayerMsg::RegisterPlayer(image_sender)) + .unwrap(); + match image_receiver.recv().unwrap() { + GLPlayerMsgForward::PlayerId(id) => (id, Some(image_receiver)), + _ => unreachable!(), + } + }) + .unwrap_or((0, None)); + + self.id.set(player_id); + self.frame_renderer.lock().unwrap().player_id = Some(player_id); + + if let Some(image_receiver) = image_receiver { + let trusted_node = Trusted::new(self); + let (task_source, canceller) = window + .task_manager() + .media_element_task_source_with_canceller(); + ROUTER.add_route( + image_receiver.to_opaque(), + Box::new(move |message| { + let msg = message.to().unwrap(); + let this = trusted_node.clone(); + if let Err(err) = task_source.queue_with_canceller( + task!(handle_glplayer_message: move || { + trace!("GLPlayer message {:?}", msg); + let frame_renderer = this.root().frame_renderer.clone(); + + match msg { + GLPlayerMsgForward::Lock(sender) => { + frame_renderer + .lock() + .unwrap() + .current_frame_holder + .as_mut() + .map(|holder| { + holder.lock(); + sender.send(holder.get()).unwrap(); + }); + }, + GLPlayerMsgForward::Unlock() => { + frame_renderer + .lock() + .unwrap() + .current_frame_holder + .as_mut() + .map(|holder| holder.unlock()); + }, + _ => (), + } + }), + &canceller, + ) { + warn!("Could not queue GL player message handler task {:?}", err); + } + }), + ); + } + Ok(()) } @@ -1585,6 +1726,16 @@ impl HTMLMediaElement { impl Drop for HTMLMediaElement { fn drop(&mut self) { + let window = window_from_node(self); + window.get_player_context().glplayer_chan.map(|pipeline| { + if let Err(err) = pipeline + .channel() + .send(GLPlayerMsg::UnregisterPlayer(self.id.get())) + { + warn!("GLPlayer disappeared!: {:?}", err); + } + }); + if let Some(ref player) = *self.player.borrow() { if let Err(err) = player.shutdown() { warn!("Error shutting down player {:?}", err); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 596fd711599..8ca1e324511 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -85,6 +85,7 @@ use js::jsval::JSVal; use js::jsval::UndefinedValue; use js::rust::wrappers::JS_DefineProperty; use js::rust::HandleValue; +use media::WindowGLContext; use msg::constellation_msg::PipelineId; use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse}; use net_traits::image_cache::{PendingImageId, PendingImageResponse}; @@ -288,6 +289,7 @@ pub struct Window { /// Flag to identify whether mutation observers are present(true)/absent(false) exists_mut_observer: Cell, + /// Webrender API Sender #[ignore_malloc_size_of = "defined in webrender_api"] webrender_api_sender: RenderApiSender, @@ -317,6 +319,10 @@ pub struct Window { /// Replace unpaired surrogates in DOM strings with U+FFFD. /// See replace_surrogates: bool, + + /// Window's GL context from application + #[ignore_malloc_size_of = "defined in script_thread"] + player_context: WindowGLContext, } impl Window { @@ -480,6 +486,10 @@ impl Window { pub fn unminify_js(&self) -> bool { self.unminify_js } + + pub fn get_player_context(&self) -> WindowGLContext { + self.player_context.clone() + } } // https://html.spec.whatwg.org/multipage/#atob @@ -2073,6 +2083,7 @@ impl Window { is_headless: bool, replace_surrogates: bool, user_agent: Cow<'static, str>, + player_context: WindowGLContext, ) -> DomRoot { let layout_rpc: Box = { let (rpc_send, rpc_recv) = unbounded(); @@ -2153,6 +2164,7 @@ impl Window { unminify_js, userscripts_path, replace_surrogates, + player_context, }); unsafe { WindowBinding::Wrap(runtime.cx(), win) } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 5afe7f1c2d1..e6d91a1bc2e 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -103,6 +103,7 @@ use js::jsapi::{JSContext, JS_SetWrapObjectCallbacks}; use js::jsapi::{JSTracer, SetWindowProxyClass}; use js::jsval::UndefinedValue; use js::rust::ParentRuntime; +use media::WindowGLContext; use metrics::{PaintTimeMetrics, MAX_TASK_NS}; use mime::{self, Mime}; use msg::constellation_msg::{ @@ -646,7 +647,7 @@ pub struct ScriptThread { /// The Webrender Document ID associated with this thread. webrender_document: DocumentId, - /// FIXME(victor): + /// Webrender API sender. webrender_api_sender: RenderApiSender, /// Periodically print out on which events script threads spend their processing time. @@ -678,6 +679,9 @@ pub struct ScriptThread { /// An optional string allowing the user agent to be set for testing. user_agent: Cow<'static, str>, + + /// Application window's GL Context for Media player + player_context: WindowGLContext, } /// In the event of thread panic, all data on the stack runs its destructor. However, there @@ -1239,6 +1243,7 @@ impl ScriptThread { headless, replace_surrogates, user_agent, + player_context: state.player_context, } } @@ -2981,6 +2986,7 @@ impl ScriptThread { self.headless, self.replace_surrogates, self.user_agent.clone(), + self.player_context.clone(), ); // Initialize the browsing context for the window. diff --git a/components/script_traits/Cargo.toml b/components/script_traits/Cargo.toml index 20f58e6bb27..5cc35639ae0 100644 --- a/components/script_traits/Cargo.toml +++ b/components/script_traits/Cargo.toml @@ -27,6 +27,7 @@ keyboard-types = "0.4.3" libc = "0.2" malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = "0.1" +media = {path = "../media"} msg = {path = "../msg"} net_traits = {path = "../net_traits"} pixels = {path = "../pixels"} diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 40e710846f9..4b87a104a4d 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -34,6 +34,7 @@ use ipc_channel::Error as IpcError; use keyboard_types::webdriver::Event as WebDriverInputEvent; use keyboard_types::{CompositionEvent, KeyboardEvent}; use libc::c_void; +use media::WindowGLContext; use msg::constellation_msg::BackgroundHangMonitorRegister; use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId}; use msg::constellation_msg::{PipelineNamespaceId, TopLevelBrowsingContextId, TraversalDirection}; @@ -636,6 +637,8 @@ pub struct InitialScriptState { pub webrender_api_sender: RenderApiSender, /// Flag to indicate if the layout thread is busy handling a request. pub layout_is_busy: Arc, + /// Application window's GL Context for Media player + pub player_context: WindowGLContext, } /// This trait allows creating a `ScriptThread` without depending on the `script` diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index b717adce9e4..d00f11459b4 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -58,6 +58,7 @@ keyboard-types = "0.4" layout_thread_2013 = {path = "../layout_thread", optional = true} layout_thread_2020 = {path = "../layout_thread_2020", optional = true} log = "0.4" +media = {path = "../media"} msg = {path = "../msg"} net = {path = "../net"} net_traits = {path = "../net_traits"} @@ -74,6 +75,7 @@ style = {path = "../style", features = ["servo"]} style_traits = {path = "../style_traits", features = ["servo"]} webrender = {git = "https://github.com/servo/webrender"} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} +webrender_traits = {path = "../webrender_traits"} webdriver_server = {path = "../webdriver_server", optional = true} webvr = {path = "../webvr"} webvr_traits = {path = "../webvr_traits"} diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 9ef5013a7b2..0ba8fa478ef 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -35,6 +35,7 @@ pub use euclid; pub use gfx; pub use ipc_channel; pub use layout_thread; +pub use media; pub use msg; pub use net; pub use net_traits; @@ -49,6 +50,7 @@ pub use servo_url; pub use style; pub use style_traits; pub use webrender_api; +pub use webrender_traits; pub use webvr; pub use webvr_traits; @@ -93,6 +95,7 @@ use gaol::sandbox::{ChildSandbox, ChildSandboxMethods}; use gfx::font_cache_thread::FontCacheThread; use ipc_channel::ipc::{self, IpcSender}; use log::{Log, Metadata, Record}; +use media::{GLPlayerThreads, WindowGLContext}; use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId}; use net::resource_thread::new_resource_threads; use net_traits::IpcSend; @@ -103,12 +106,14 @@ use profile_traits::time; use script_traits::{ConstellationMsg, SWManagerSenders, ScriptToConstellationChan}; use servo_config::opts; use servo_config::{pref, prefs}; +use servo_media::player::context::GlContext; use servo_media::ServoMedia; use std::borrow::Cow; use std::cmp::max; use std::path::PathBuf; use std::rc::Rc; use webrender::{RendererKind, ShaderPrecacheFlags}; +use webrender_traits::{WebrenderExternalImageHandlers, WebrenderImageHandlerType}; use webvr::{VRServiceManager, WebVRCompositorHandler, WebVRThread}; pub use gleam::gl; @@ -303,6 +308,13 @@ where None }; + let player_context = WindowGLContext { + gl_context: window.get_gl_context(), + native_display: window.get_native_display(), + gl_api: window.get_gl_api(), + glplayer_chan: None, + }; + // Create the constellation, which maintains the engine // pipelines, including the script and layout threads, as well // as the navigation context. @@ -321,6 +333,7 @@ where window.gl(), webvr_services, webxr_registry, + player_context, ); // Send the constellation's swmanager sender to service worker manager thread @@ -631,6 +644,7 @@ fn create_constellation( window_gl: Rc, webvr_services: Option, webxr_registry: webxr_api::Registry, + player_context: WindowGLContext, ) -> (Sender, SWManagerSenders) { // Global configuration options, parsed from the command line. let opts = opts::get(); @@ -676,6 +690,9 @@ fn create_constellation( GLContextFactory::current_native_handle(&compositor_proxy) }; + let (external_image_handlers, external_images) = WebrenderExternalImageHandlers::new(); + let mut external_image_handlers = Box::new(external_image_handlers); + // Initialize WebGL Thread entry point. let webgl_threads = gl_factory.map(|factory| { let (webgl_threads, image_handler, output_handler) = WebGLThreads::new( @@ -683,10 +700,11 @@ fn create_constellation( window_gl, webrender_api_sender.clone(), webvr_compositor.map(|c| c as Box<_>), + external_images.clone(), ); // Set webrender external image handler for WebGL textures - webrender.set_external_image_handler(image_handler); + external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL); // Set DOM to texture handler, if enabled. if let Some(output_handler) = output_handler { @@ -696,6 +714,22 @@ fn create_constellation( webgl_threads }); + let glplayer_threads = match player_context.gl_context { + GlContext::Unknown => None, + _ => { + let (glplayer_threads, image_handler) = GLPlayerThreads::new(external_images); + external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::Media); + Some(glplayer_threads) + }, + }; + + webrender.set_external_image_handler(external_image_handlers); + + let player_context = WindowGLContext { + glplayer_chan: glplayer_threads.as_ref().map(|threads| threads.pipeline()), + ..player_context + }; + let initial_state = InitialConstellationState { compositor_proxy, embedder_proxy, @@ -712,6 +746,8 @@ fn create_constellation( webgl_threads, webvr_chan, webxr_registry, + glplayer_threads, + player_context, }; let (constellation_chan, from_swmanager_sender) = Constellation::< script_layout_interface::message::Msg, diff --git a/components/webrender_traits/Cargo.toml b/components/webrender_traits/Cargo.toml new file mode 100644 index 00000000000..01e2ca2f2e3 --- /dev/null +++ b/components/webrender_traits/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "webrender_traits" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +edition = "2018" +publish = false + +[lib] +name = "webrender_traits" +path = "lib.rs" + +[dependencies] +euclid = "0.19" +webrender = {git = "https://github.com/servo/webrender"} +webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} + diff --git a/components/webrender_traits/lib.rs b/components/webrender_traits/lib.rs new file mode 100644 index 00000000000..40d9f0956c3 --- /dev/null +++ b/components/webrender_traits/lib.rs @@ -0,0 +1,149 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#![deny(unsafe_code)] + +use euclid::Size2D; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + +/// This trait is used as a bridge between the different GL clients +/// in Servo that handles WebRender ExternalImages and the WebRender +/// ExternalImageHandler API. +// +/// This trait is used to notify lock/unlock messages and get the +/// required info that WR needs. +pub trait WebrenderExternalImageApi { + fn lock(&mut self, id: u64) -> (u32, Size2D); + fn unlock(&mut self, id: u64); +} + +/// Type of Webrender External Image Handler. +pub enum WebrenderImageHandlerType { + WebGL, + Media, +} + +/// List of Webrender external images to be shared among all external image +/// consumers (WebGL, Media). +/// It ensures that external image identifiers are unique. +pub struct WebrenderExternalImageRegistry { + /// Map of all generated external images. + external_images: HashMap, + /// Id generator for the next external image identifier. + next_image_id: u64, +} + +impl WebrenderExternalImageRegistry { + pub fn new() -> Self { + Self { + external_images: HashMap::new(), + next_image_id: 0, + } + } + + pub fn next_id( + &mut self, + handler_type: WebrenderImageHandlerType, + ) -> webrender_api::ExternalImageId { + self.next_image_id += 1; + let key = webrender_api::ExternalImageId(self.next_image_id); + self.external_images.insert(key, handler_type); + key + } + + pub fn remove(&mut self, key: &webrender_api::ExternalImageId) { + self.external_images.remove(key); + } + + pub fn get(&self, key: &webrender_api::ExternalImageId) -> Option<&WebrenderImageHandlerType> { + self.external_images.get(key) + } +} + +/// WebRender External Image Handler implementation. +pub struct WebrenderExternalImageHandlers { + /// WebGL handler. + webgl_handler: Option>, + /// Media player handler. + media_handler: Option>, + /// Webrender external images. + external_images: Arc>, +} + +impl WebrenderExternalImageHandlers { + pub fn new() -> (Self, Arc>) { + let external_images = Arc::new(Mutex::new(WebrenderExternalImageRegistry::new())); + ( + Self { + webgl_handler: None, + media_handler: None, + external_images: external_images.clone(), + }, + external_images, + ) + } + + pub fn set_handler( + &mut self, + handler: Box, + handler_type: WebrenderImageHandlerType, + ) { + match handler_type { + WebrenderImageHandlerType::WebGL => self.webgl_handler = Some(handler), + WebrenderImageHandlerType::Media => self.media_handler = Some(handler), + } + } +} + +impl webrender::ExternalImageHandler for WebrenderExternalImageHandlers { + /// Lock the external image. Then, WR could start to read the + /// image content. + /// The WR client should not change the image content until the + /// unlock() call. + fn lock( + &mut self, + key: webrender_api::ExternalImageId, + _channel_index: u8, + _rendering: webrender_api::ImageRendering, + ) -> webrender::ExternalImage { + let external_images = self.external_images.lock().unwrap(); + let handler_type = external_images + .get(&key) + .expect("Tried to get unknown external image"); + let (texture_id, uv) = match handler_type { + WebrenderImageHandlerType::WebGL => { + let (texture_id, size) = self.webgl_handler.as_mut().unwrap().lock(key.0); + ( + texture_id, + webrender_api::TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0), + ) + }, + WebrenderImageHandlerType::Media => { + let (texture_id, size) = self.media_handler.as_mut().unwrap().lock(key.0); + ( + texture_id, + webrender_api::TexelRect::new(0.0, 0.0, size.width as f32, size.height as f32), + ) + }, + }; + webrender::ExternalImage { + uv, + source: webrender::ExternalImageSource::NativeTexture(texture_id), + } + } + + /// Unlock the external image. The WR should not read the image + /// content after this call. + fn unlock(&mut self, key: webrender_api::ExternalImageId, _channel_index: u8) { + let external_images = self.external_images.lock().unwrap(); + let handler_type = external_images + .get(&key) + .expect("Tried to get unknown external image"); + match handler_type { + WebrenderImageHandlerType::WebGL => self.webgl_handler.as_mut().unwrap().unlock(key.0), + WebrenderImageHandlerType::Media => self.media_handler.as_mut().unwrap().unlock(key.0), + }; + } +} diff --git a/ports/glutin/Cargo.toml b/ports/glutin/Cargo.toml index c52a04d0b84..e6642e17ce8 100644 --- a/ports/glutin/Cargo.toml +++ b/ports/glutin/Cargo.toml @@ -46,6 +46,7 @@ webrender_debugger = ["libservo/webrender_debugger"] [target.'cfg(not(target_os = "android"))'.dependencies] backtrace = "0.3" bitflags = "1.0" +clipboard = "0.5" crossbeam-channel = "0.3" euclid = "0.19" gleam = "0.6" @@ -56,9 +57,9 @@ libservo = {path = "../../components/servo"} libc = "0.2" log = "0.4" rust-webvr = { version = "0.13", features = ["glwindow"] } -webxr = { git = "https://github.com/servo/webxr", features = ["glwindow"] } +servo-media = {git = "https://github.com/servo/media"} tinyfiledialogs = "3.0" -clipboard = "0.5" +webxr = { git = "https://github.com/servo/webxr", features = ["glwindow"] } [target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies] image = "0.21" diff --git a/ports/glutin/context.rs b/ports/glutin/context.rs index 5e5e86cb461..39749f82b90 100644 --- a/ports/glutin/context.rs +++ b/ports/glutin/context.rs @@ -2,7 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use glutin::{WindowedContext, NotCurrent, PossiblyCurrent}; +use glutin::os::ContextTraitExt; +use glutin::{NotCurrent, PossiblyCurrent, WindowedContext}; +use servo_media::player::context::GlContext as RawContext; +use std::os::raw; pub enum GlContext { Current(WindowedContext), @@ -71,4 +74,85 @@ impl GlContext { GlContext::None => unreachable!(), }; } + #[allow(unreachable_code, unused_variables)] + pub fn raw_context(&self) -> RawContext { + match self { + GlContext::Current(c) => { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + { + use glutin::os::unix::RawHandle; + + let raw_handle = unsafe { c.raw_handle() }; + return match raw_handle { + RawHandle::Egl(handle) => RawContext::Egl(handle as usize), + RawHandle::Glx(handle) => RawContext::Glx(handle as usize), + }; + } + + #[cfg(target_os = "windows")] + { + use glutin::os::windows::RawHandle; + + let raw_handle = unsafe { c.raw_handle() }; + return match raw_handle { + RawHandle::Egl(handle) => RawContext::Egl(handle as usize), + // @TODO(victor): RawContext::Wgl in servo-media + RawHandle::Wgl(_) => unimplemented!(), + } + } + + #[cfg(target_os = "android")] + { + let raw_handle = unsafe { c.raw_handle() }; + return RawContext::Egl(raw_handle as usize); + } + + #[cfg(target_os = "macos")] + return unimplemented!(); // @TODO(victor): RawContext::Cocoa in servo-media + + #[cfg(not(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "windows", + target_os = "android", + target_os = "macos", + )))] + unimplemented!() + } + GlContext::NotCurrent(_) => { + error!("Context is not current."); + RawContext::Unknown + } + GlContext::None => unreachable!(), + } + } + + #[allow(dead_code)] + pub fn egl_display(&self) -> Option<*const raw::c_void> { + match self { + GlContext::Current(c) => unsafe { c.get_egl_display() }, + GlContext::NotCurrent(_) => { + error!("Context is not current."); + None + }, + GlContext::None => unreachable!(), + } + } + + pub fn get_api(&self) -> glutin::Api { + match self { + GlContext::Current(c) => c.get_api(), + GlContext::NotCurrent(c) => c.get_api(), + GlContext::None => unreachable!(), + } + } } diff --git a/ports/glutin/headed_window.rs b/ports/glutin/headed_window.rs index 3026170645e..c19d2bdb63b 100644 --- a/ports/glutin/headed_window.rs +++ b/ports/glutin/headed_window.rs @@ -13,9 +13,9 @@ use gleam::gl; use glutin::dpi::{LogicalPosition, LogicalSize, PhysicalSize}; #[cfg(target_os = "macos")] use glutin::os::macos::{ActivationPolicy, WindowBuilderExt}; +use glutin::Api; #[cfg(any(target_os = "linux", target_os = "windows"))] use glutin::Icon; -use glutin::Api; use glutin::{ElementState, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase}; #[cfg(any(target_os = "linux", target_os = "windows"))] use image; @@ -24,12 +24,13 @@ use servo::compositing::windowing::{AnimationState, MouseWindowEvent, WindowEven use servo::compositing::windowing::{EmbedderCoordinates, WindowMethods}; use servo::embedder_traits::Cursor; use servo::script_traits::{TouchEventType, WheelMode, WheelDelta}; -use servo::servo_config::opts; +use servo::servo_config::{opts, pref}; use servo::servo_geometry::DeviceIndependentPixel; use servo::style_traits::DevicePixel; use servo::webrender_api::{ DeviceIntPoint, DeviceIntRect, DeviceIntSize, FramebufferIntSize, ScrollLocation, }; +use servo_media::player::context::{GlApi, GlContext as PlayerGLContext, NativeDisplay}; use std::cell::{Cell, RefCell}; use std::mem; use std::rc::Rc; @@ -524,6 +525,102 @@ impl WindowMethods for Window { fn prepare_for_composite(&self) { self.gl_context.borrow_mut().make_current(); } + + fn get_gl_context(&self) -> PlayerGLContext { + if pref!(media.glvideo.enabled) { + self.gl_context.borrow().raw_context() + } else { + PlayerGLContext::Unknown + } + } + + fn get_native_display(&self) -> NativeDisplay { + if !pref!(media.glvideo.enabled) { + return NativeDisplay::Unknown; + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "windows", + target_os = "android", + ))] + let native_display = { + if let Some(display) = self.gl_context.borrow().egl_display() { + NativeDisplay::Egl(display as usize) + } else { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + { + use glutin::os::unix::WindowExt; + + if let Some(display) = self.gl_context.borrow().window().get_wayland_display() { + NativeDisplay::Wayland(display as usize) + } else if let Some(display) = + self.gl_context.borrow().window().get_xlib_display() + { + NativeDisplay::X11(display as usize) + } else { + NativeDisplay::Unknown + } + } + + #[cfg(not(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + )))] + NativeDisplay::Unknown + } + }; + + #[cfg(not(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "windows", + target_os = "android", + )))] + let native_display = NativeDisplay::Unknown; + + native_display + } + + fn get_gl_api(&self) -> GlApi { + let api = self.gl_context.borrow().get_api(); + + let version = self.gl.get_string(gl::VERSION); + let version = version.trim_start_matches("OpenGL ES "); + let mut values = version.split(&['.', ' '][..]); + let major = values + .next() + .and_then(|v| v.parse::().ok()) + .unwrap_or(1); + let minor = values + .next() + .and_then(|v| v.parse::().ok()) + .unwrap_or(20); + + match api { + glutin::Api::OpenGl if major >= 3 && minor >= 2 => GlApi::OpenGL3, + glutin::Api::OpenGl => GlApi::OpenGL, + glutin::Api::OpenGlEs if major > 1 => GlApi::Gles2, + glutin::Api::OpenGlEs => GlApi::Gles1, + _ => GlApi::None, + } + } } fn winit_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType { diff --git a/ports/glutin/headless_window.rs b/ports/glutin/headless_window.rs index 6357ad04a9c..63b45cc5de7 100644 --- a/ports/glutin/headless_window.rs +++ b/ports/glutin/headless_window.rs @@ -14,6 +14,7 @@ use servo::servo_config::opts; use servo::servo_geometry::DeviceIndependentPixel; use servo::style_traits::DevicePixel; use servo::webrender_api::{DeviceIntRect, FramebufferIntSize}; +use servo_media::player::context as MediaPlayerCtxt; use std::cell::Cell; #[cfg(any(target_os = "linux", target_os = "macos"))] use std::ffi::CString; @@ -195,4 +196,16 @@ impl WindowMethods for Window { } fn prepare_for_composite(&self) { } + + fn get_gl_context(&self) -> MediaPlayerCtxt::GlContext { + MediaPlayerCtxt::GlContext::Unknown + } + + fn get_native_display(&self) -> MediaPlayerCtxt::NativeDisplay { + MediaPlayerCtxt::NativeDisplay::Unknown + } + + fn get_gl_api(&self) -> MediaPlayerCtxt::GlApi { + MediaPlayerCtxt::GlApi::None + } } diff --git a/ports/libmlservo/src/lib.rs b/ports/libmlservo/src/lib.rs index b71900c17ea..56739a54f23 100644 --- a/ports/libmlservo/src/lib.rs +++ b/ports/libmlservo/src/lib.rs @@ -144,8 +144,8 @@ pub unsafe extern "C" fn init_servo( VRInitOptions::None } else { let name = String::from("Magic Leap VR Display"); - let (service, heartbeat) = - MagicLeapVRService::new(name, ctxt, gl.clone()).expect("Failed to create VR service"); + let (service, heartbeat) = MagicLeapVRService::new(name, ctxt, gl.gl_wrapper.clone()) + .expect("Failed to create VR service"); let service = Box::new(service); let heartbeat = Box::new(heartbeat); VRInitOptions::VRService(service, heartbeat) @@ -157,6 +157,8 @@ pub unsafe extern "C" fn init_servo( enable_subpixel_text_antialiasing: false, vr_init, coordinates, + gl_context_pointer: Some(gl.gl_context), + native_display_pointer: Some(gl.display), }; let wakeup = Box::new(EventLoopWakerInstance); let shut_down_complete = Rc::new(Cell::new(false)); @@ -172,7 +174,7 @@ pub unsafe extern "C" fn init_servo( keyboard, }); info!("Starting servo"); - simpleservo::init(opts, gl, wakeup, callbacks).expect("error initializing Servo"); + simpleservo::init(opts, gl.gl_wrapper, wakeup, callbacks).expect("error initializing Servo"); let result = Box::new(ServoInstance { scroll_state: ScrollState::TriggerUp, diff --git a/ports/libsimpleservo/api/Cargo.toml b/ports/libsimpleservo/api/Cargo.toml index 8efa8d6086a..375023232c9 100644 --- a/ports/libsimpleservo/api/Cargo.toml +++ b/ports/libsimpleservo/api/Cargo.toml @@ -9,6 +9,7 @@ publish = false [dependencies] libservo = { path = "../../../components/servo" } log = "0.4" +servo-media = { git = "https://github.com/servo/media" } [target.'cfg(not(target_os = "macos"))'.dependencies] libc = "0.2" diff --git a/ports/libsimpleservo/api/src/gl_glue.rs b/ports/libsimpleservo/api/src/gl_glue.rs index 126de03789d..bc982959930 100644 --- a/ports/libsimpleservo/api/src/gl_glue.rs +++ b/ports/libsimpleservo/api/src/gl_glue.rs @@ -18,6 +18,7 @@ pub mod egl { pub type khronos_uint64_t = libc::uint64_t; pub type khronos_ssize_t = libc::c_long; pub type EGLint = libc::int32_t; + pub type EGLContext = *const libc::c_void; pub type EGLNativeDisplayType = *const libc::c_void; pub type EGLNativePixmapType = *const libc::c_void; pub type NativeDisplayType = EGLNativeDisplayType; @@ -26,12 +27,18 @@ pub mod egl { include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); - pub fn init() -> Result { + pub struct EGLInitResult { + pub gl_wrapper: crate::gl_glue::ServoGl, + pub gl_context: EGLContext, + pub display: EGLNativeDisplayType, + } + + pub fn init() -> Result { info!("Loading EGL..."); unsafe { let egl = Egl; - let d = egl.GetCurrentDisplay(); - egl.SwapInterval(d, 1); + let display = egl.GetCurrentDisplay(); + egl.SwapInterval(display, 1); let egl = GlesFns::load_with(|addr| { let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); @@ -39,7 +46,11 @@ pub mod egl { egl.GetProcAddress(addr) as *const c_void }); info!("EGL loaded"); - Ok(egl) + Ok(EGLInitResult { + gl_wrapper: egl, + gl_context: Egl.GetCurrentContext(), + display, + }) } } } diff --git a/ports/libsimpleservo/api/src/lib.rs b/ports/libsimpleservo/api/src/lib.rs index 1adf4d14eea..2935852ad86 100644 --- a/ports/libsimpleservo/api/src/lib.rs +++ b/ports/libsimpleservo/api/src/lib.rs @@ -25,7 +25,7 @@ use servo::servo_url::ServoUrl; use servo::webrender_api::{DevicePixel, FramebufferPixel, ScrollLocation}; use servo::webvr::{VRExternalShmemPtr, VRMainThreadHeartbeat, VRService, VRServiceManager}; use servo::{self, gl, BrowserId, Servo}; - +use servo_media::player::context as MediaPlayerContext; use std::cell::RefCell; use std::mem; use std::os::raw::c_void; @@ -48,6 +48,8 @@ pub struct InitOptions { pub density: f32, pub vr_init: VRInitOptions, pub enable_subpixel_text_antialiasing: bool, + pub gl_context_pointer: Option<*const c_void>, + pub native_display_pointer: Option<*const c_void>, } pub enum VRInitOptions { @@ -187,6 +189,8 @@ pub fn init( host_callbacks: callbacks, coordinates: RefCell::new(init_opts.coordinates), density: init_opts.density, + gl_context_pointer: init_opts.gl_context_pointer, + native_display_pointer: init_opts.native_display_pointer, }); let embedder_callbacks = Box::new(ServoEmbedderCallbacks { @@ -583,6 +587,8 @@ struct ServoWindowCallbacks { host_callbacks: Box, coordinates: RefCell, density: f32, + gl_context_pointer: Option<*const c_void>, + native_display_pointer: Option<*const c_void>, } impl EmbedderMethods for ServoEmbedderCallbacks { @@ -643,6 +649,24 @@ impl WindowMethods for ServoWindowCallbacks { hidpi_factor: TypedScale::new(self.density), } } + + fn get_gl_context(&self) -> MediaPlayerContext::GlContext { + match self.gl_context_pointer { + Some(context) => MediaPlayerContext::GlContext::Egl(context as usize), + None => MediaPlayerContext::GlContext::Unknown, + } + } + + fn get_native_display(&self) -> MediaPlayerContext::NativeDisplay { + match self.native_display_pointer { + Some(display) => MediaPlayerContext::NativeDisplay::Egl(display as usize), + None => MediaPlayerContext::NativeDisplay::Unknown, + } + } + + fn get_gl_api(&self) -> MediaPlayerContext::GlApi { + MediaPlayerContext::GlApi::Gles2 + } } struct ResourceReaderInstance; diff --git a/ports/libsimpleservo/capi/src/lib.rs b/ports/libsimpleservo/capi/src/lib.rs index 89d621e3ffe..e415550e5f6 100644 --- a/ports/libsimpleservo/capi/src/lib.rs +++ b/ports/libsimpleservo/capi/src/lib.rs @@ -93,16 +93,18 @@ fn init_logger() { crate::env_logger::init(); } -fn init( +unsafe fn init( opts: CInitOptions, gl: gl_glue::ServoGl, + gl_context: Option<*const c_void>, + display: Option<*const c_void>, wakeup: extern "C" fn(), callbacks: CHostCallbacks, ) { init_logger(); let args = if !opts.args.is_null() { - let args = unsafe { CStr::from_ptr(opts.args) }; + let args = CStr::from_ptr(opts.args); args.to_str() .unwrap_or("") .split(' ') @@ -112,7 +114,7 @@ fn init( vec![] }; - let url = unsafe { CStr::from_ptr(opts.url) }; + let url = CStr::from_ptr(opts.url); let url = url.to_str().map(|s| s.to_string()).ok(); let coordinates = Coordinates::new(0, 0, opts.width, opts.height, opts.width, opts.height); @@ -128,6 +130,8 @@ fn init( VRInitOptions::VRExternal(opts.vr_pointer) }, enable_subpixel_text_antialiasing: opts.enable_subpixel_text_antialiasing, + gl_context_pointer: gl_context, + native_display_pointer: display, }; let wakeup = Box::new(WakeupCallback::new(wakeup)); @@ -145,7 +149,16 @@ pub extern "C" fn init_with_egl( ) { init_logger(); let gl = gl_glue::egl::init().unwrap(); - init(opts, gl, wakeup, callbacks) + unsafe { + init( + opts, + gl.gl_wrapper, + Some(gl.gl_context), + Some(gl.display), + wakeup, + callbacks, + ) + } } #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] @@ -157,7 +170,7 @@ pub extern "C" fn init_with_gl( ) { init_logger(); let gl = gl_glue::gl::init().unwrap(); - init(opts, gl, wakeup, callbacks) + unsafe { init(opts, gl, None, None, wakeup, callbacks) } } #[no_mangle] diff --git a/ports/libsimpleservo/jniapi/src/lib.rs b/ports/libsimpleservo/jniapi/src/lib.rs index d342a1a6c29..b392be62f66 100644 --- a/ports/libsimpleservo/jniapi/src/lib.rs +++ b/ports/libsimpleservo/jniapi/src/lib.rs @@ -52,7 +52,7 @@ pub fn Java_org_mozilla_servoview_JNIServo_init( opts: JObject, callbacks_obj: JObject, ) { - let (opts, log, log_str) = match get_options(&env, opts) { + let (mut opts, log, log_str) = match get_options(&env, opts) { Ok((opts, log, log_str)) => (opts, log, log_str), Err(err) => { throw(&env, &err); @@ -104,9 +104,11 @@ pub fn Java_org_mozilla_servoview_JNIServo_init( let wakeup = Box::new(WakeupCallback::new(callbacks_ref.clone(), &env)); let callbacks = Box::new(HostCallbacks::new(callbacks_ref, &env)); - if let Err(err) = - gl_glue::egl::init().and_then(|gl| simpleservo::init(opts, gl, wakeup, callbacks)) - { + if let Err(err) = gl_glue::egl::init().and_then(|egl_init| { + opts.gl_context_pointer = Some(egl_init.gl_context); + opts.native_display_pointer = Some(egl_init.display); + simpleservo::init(opts, egl_init.gl_wrapper, wakeup, callbacks) + }) { throw(&env, err) }; } @@ -726,6 +728,8 @@ fn get_options(env: &JNIEnv, opts: JObject) -> Result<(InitOptions, bool, Option } else { VRInitOptions::VRExternal(vr_pointer) }, + gl_context_pointer: None, + native_display_pointer: None, }; Ok((opts, log, log_str)) } diff --git a/resources/prefs.json b/resources/prefs.json index 22c4e22b4db..8e7efaf6069 100644 --- a/resources/prefs.json +++ b/resources/prefs.json @@ -84,6 +84,7 @@ "layout.threads": 3, "layout.viewport.enabled": false, "layout.writing-mode.enabled": false, + "media.glvideo.enabled": false, "media.testing.enabled": false, "network.http-cache.disabled": false, "network.mime.sniff": false,