Auto merge of #23483 - ceyusa:player-context, r=jdm

Media player rendering with GL textures

These patches pass the application's OpenGL raw context  and the its native display address to the media player, in order to create an internal wrapped context, thus it will generate video frames as textures.

For now only EGL from glutin-based app and android are in place, though tested only in Linux glutin app.

This PR also renders the generated frame textures by Servo/Media and renders them by using a thread that connects Webrenderer's ExternalImageHandler and each instantiated player. **By now, these patches, disable the WebGL rendering**. We need to provide a ExternalImageHandler demuxer.

This PR depends on https://github.com/servo/media/pull/270

- [X]  `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- This PR fixes #22300 and fixes #22920

In order to test it you must launch servo as

`./mach run -- --pref media.glvideo.enabled [...]`

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23483)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-07-04 23:28:28 -04:00 committed by GitHub
commit 0dc17af7f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1282 additions and 128 deletions

55
Cargo.lock generated
View file

@ -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"

View file

@ -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"}

View file

@ -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<WebGLMsg>);
@ -24,9 +26,10 @@ impl WebGLThreads {
webrender_gl: Rc<dyn gl::Gl>,
webrender_api_sender: webrender_api::RenderApiSender,
webvr_compositor: Option<Box<dyn WebVRRenderHandler>>,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
) -> (
WebGLThreads,
Box<dyn webrender::ExternalImageHandler>,
Box<dyn WebrenderExternalImageApi>,
Option<Box<dyn webrender::OutputImageHandler>>,
) {
// 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<i32>) {
impl WebrenderExternalImageApi for WebGLExternalImages {
fn lock(&mut self, id: u64) -> (u32, Size2D<i32>) {
// 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();
}
}

View file

@ -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<VR: WebVRRenderHandler + 'static> {
cached_context_info: FnvHashMap<WebGLContextId, WebGLContextInfo>,
/// Current bound context.
bound_context_id: Option<WebGLContextId>,
/// Id generator for new WebGLContexts.
next_webgl_id: usize,
/// Handler user to send WebVR commands.
webvr_compositor: Option<VR>,
/// Texture ids and sizes used in DOM to texture outputs.
dom_outputs: FnvHashMap<webrender_api::PipelineId, DOMToTextureData>,
/// List of registered webrender external images.
/// We use it to get an unique ID for new WebGLContexts.
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
}
impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
@ -71,6 +74,7 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
gl_factory: GLContextFactory,
webrender_api_sender: webrender_api::RenderApiSender,
webvr_compositor: Option<VR>,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
) -> Self {
WebGLThread {
gl_factory,
@ -78,9 +82,9 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
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<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
gl_factory: GLContextFactory,
webrender_api_sender: webrender_api::RenderApiSender,
webvr_compositor: Option<VR>,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
) -> WebGLSender<WebGLMsg> {
let (sender, receiver) = webgl_channel::<WebGLMsg>().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<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
})
.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<T>` 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<i32>);
fn unlock(&mut self, ctx_id: WebGLContextId);
}
/// WebRender External Image Handler implementation
pub struct WebGLExternalImageHandler<T: WebGLExternalImageApi> {
handler: T,
}
impl<T: WebGLExternalImageApi> WebGLExternalImageHandler<T> {
pub fn new(handler: T) -> Self {
Self { handler: handler }
}
}
impl<T: WebGLExternalImageApi> webrender::ExternalImageHandler for WebGLExternalImageHandler<T> {
/// 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,

View file

@ -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"

View file

@ -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 {

View file

@ -446,6 +446,9 @@ mod gen {
}
},
media: {
glvideo: {
enabled: bool,
},
testing: {
enabled: bool,
}

View file

@ -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"}

View file

@ -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<Message, LTF, STF> {
/// 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<GLPlayerThreads>,
/// 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<GLPlayerThreads>,
/// 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);

View file

@ -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<WebGLPipeline>,
webvr_chan: Option<IpcSender<WebVRMsg>>,
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,

View file

@ -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"}

169
components/media/lib.rs Normal file
View file

@ -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<i32>, 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<GLPlayerMsgForward>),
/// 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<i32>, 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<GLPlayerPipeline>,
}
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<GLPlayerMsg>);
impl GLPlayerThreads {
pub fn new(
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
) -> (GLPlayerThreads, Box<dyn WebrenderExternalImageApi>) {
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<dyn gl::Gl>,
glplayer_channel: GLPlayerSender<GLPlayerMsg>,
// Used to avoid creating a new channel on each received WebRender
// request.
lock_channel: (
GLPlayerSender<(u32, Size2D<i32>, usize)>,
GLPlayerReceiver<(u32, Size2D<i32>, usize)>,
),
}
impl GLPlayerExternalImages {
fn new(channel: GLPlayerSender<GLPlayerMsg>) -> Self {
Self {
glplayer_channel: channel,
lock_channel: glplayer_channel().unwrap(),
}
}
}
impl WebrenderExternalImageApi for GLPlayerExternalImages {
fn lock(&mut self, id: u64) -> (u32, Size2D<i32>) {
// 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();
}
}

View file

@ -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<T> = ipc_channel::ipc::IpcSender<T>;
pub type GLPlayerReceiver<T> = ipc_channel::ipc::IpcReceiver<T>;
pub fn glplayer_channel<T: Serialize + for<'de> Deserialize<'de>>(
) -> Result<(GLPlayerSender<T>, GLPlayerReceiver<T>), io::Error> {
ipc_channel::ipc::channel()
}

View file

@ -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<T: Serialize> {
Ipc(ipc::GLPlayerSender<T>),
Mpsc(mpsc::GLPlayerSender<T>),
}
impl<T> Clone for GLPlayerSender<T>
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<T: Serialize> fmt::Debug for GLPlayerSender<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "GLPlayerSender(..)")
}
}
impl<T: Serialize> GLPlayerSender<T> {
#[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<T>
where
T: for<'de> Deserialize<'de> + Serialize,
{
Ipc(ipc::GLPlayerReceiver<T>),
Mpsc(mpsc::GLPlayerReceiver<T>),
}
impl<T> GLPlayerReceiver<T>
where
T: for<'de> Deserialize<'de> + Serialize,
{
pub fn recv(&self) -> Result<T, ()> {
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<T>() -> Result<(GLPlayerSender<T>, GLPlayerReceiver<T>), ()>
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<GLPlayerMsg>);
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()
}
}

View file

@ -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<T> Serialize for $name<T> {
fn serialize<S: Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
unreachable!();
}
}
impl<'a, T> Deserialize<'a> for $name<T> {
fn deserialize<D>(_: D) -> Result<$name<T>, D::Error>
where
D: Deserializer<'a>,
{
unreachable!();
}
}
};
}
pub struct GLPlayerSender<T>(mpsc::Sender<T>);
pub struct GLPlayerReceiver<T>(mpsc::Receiver<T>);
impl<T> Clone for GLPlayerSender<T> {
fn clone(&self) -> Self {
GLPlayerSender(self.0.clone())
}
}
impl<T> GLPlayerSender<T> {
#[inline]
pub fn send(&self, data: T) -> Result<(), mpsc::SendError<T>> {
self.0.send(data)
}
}
impl<T> GLPlayerReceiver<T> {
#[inline]
pub fn recv(&self) -> Result<T, mpsc::RecvError> {
self.0.recv()
}
}
pub fn glplayer_channel<T>() -> Result<(GLPlayerSender<T>, GLPlayerReceiver<T>), ()> {
let (sender, receiver) = mpsc::channel();
Ok((GLPlayerSender(sender), GLPlayerReceiver(receiver)))
}
unreachable_serializable!(GLPlayerReceiver);
unreachable_serializable!(GLPlayerSender);

View file

@ -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<u64, GLPlayerSender<GLPlayerMsgForward>>,
/// List of registered webrender external images.
/// We use it to get an unique ID for new players.
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
}
impl GLPlayerThread {
pub fn new(external_images: Arc<Mutex<WebrenderExternalImageRegistry>>) -> Self {
GLPlayerThread {
players: Default::default(),
external_images,
}
}
pub fn start(
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
) -> GLPlayerSender<GLPlayerMsg> {
let (sender, receiver) = glplayer_channel::<GLPlayerMsg>().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
}
}

View file

@ -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"

View file

@ -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<f64>, Transform2D<f32>, Transform3D<f64>)
unsafe_no_jsmanaged_fields!(Point2D<f32>, Vector2D<f32>, Rect<Au>);
unsafe_no_jsmanaged_fields!(Rect<f32>, RigidTransform3D<f64>);
unsafe_no_jsmanaged_fields!(CascadeData);
unsafe_no_jsmanaged_fields!(WindowGLContext);
unsafe impl<'a> JSTraceable for &'a str {
#[inline]

View file

@ -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<i32>, 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<u64>,
api: RenderApi,
current_frame: Option<(ImageKey, i32, i32)>,
old_frame: Option<ImageKey>,
very_old_frame: Option<ImageKey>,
current_frame_holder: Option<FrameHolder>,
}
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<Timespec>,
/// Latest fetch request context.
current_fetch_context: DomRefCell<Option<HTMLMediaElementFetchContext>>,
/// Player Id reported the player thread
id: Cell<u64>,
}
/// <https://html.spec.whatwg.org/multipage/#dom-media-networkstate>
@ -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::<PlayerEvent>().unwrap();
let renderer: Option<Arc<Mutex<dyn FrameRenderer>>> = 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::<GLPlayerMsgForward>().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);

View file

@ -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<bool>,
/// 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 <https://github.com/servo/servo/issues/6564>
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<Self> {
let layout_rpc: Box<dyn LayoutRPC + Send> = {
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) }

View file

@ -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.

View file

@ -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"}

View file

@ -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<AtomicBool>,
/// Application window's GL Context for Media player
pub player_context: WindowGLContext,
}
/// This trait allows creating a `ScriptThread` without depending on the `script`

View file

@ -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"}

View file

@ -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<dyn gl::Gl>,
webvr_services: Option<VRServiceManager>,
webxr_registry: webxr_api::Registry,
player_context: WindowGLContext,
) -> (Sender<ConstellationMsg>, 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,

View file

@ -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"]}

View file

@ -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<i32>);
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<webrender_api::ExternalImageId, WebrenderImageHandlerType>,
/// 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<Box<dyn WebrenderExternalImageApi>>,
/// Media player handler.
media_handler: Option<Box<dyn WebrenderExternalImageApi>>,
/// Webrender external images.
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
}
impl WebrenderExternalImageHandlers {
pub fn new() -> (Self, Arc<Mutex<WebrenderExternalImageRegistry>>) {
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<dyn WebrenderExternalImageApi>,
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),
};
}
}

View file

@ -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"

View file

@ -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<PossiblyCurrent>),
@ -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!(),
}
}
}

View file

@ -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::<u32>().ok())
.unwrap_or(1);
let minor = values
.next()
.and_then(|v| v.parse::<u32>().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 {

View file

@ -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
}
}

View file

@ -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,

View file

@ -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"

View file

@ -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<crate::gl_glue::ServoGl, &'static str> {
pub struct EGLInitResult {
pub gl_wrapper: crate::gl_glue::ServoGl,
pub gl_context: EGLContext,
pub display: EGLNativeDisplayType,
}
pub fn init() -> Result<EGLInitResult, &'static str> {
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,
})
}
}
}

View file

@ -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<dyn HostTrait>,
coordinates: RefCell<Coordinates>,
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;

View file

@ -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]

View file

@ -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))
}

View file

@ -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,