diff --git a/Cargo.lock b/Cargo.lock index c460bc9d092..4d170408695 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4549,6 +4549,7 @@ dependencies = [ "log", "serde", "servo-media", + "servo_config", "webrender_api", "webrender_traits", ] diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 0debfc8f18f..06f872992a2 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -131,7 +131,7 @@ use ipc_channel::router::ROUTER; use ipc_channel::Error as IpcError; use keyboard_types::webdriver::Event as WebDriverInputEvent; use log::{debug, error, info, trace, warn}; -use media::{GLPlayerThreads, WindowGLContext}; +use media::WindowGLContext; use net_traits::pub_domains::reg_host; use net_traits::request::Referrer; use net_traits::storage_thread::{StorageThreadMsg, StorageType}; @@ -465,12 +465,6 @@ pub struct Constellation { /// If True, exits on thread failure instead of displaying about:failure hard_fail: 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, - /// Pipeline ID of the active media session. active_media_session: Option, @@ -528,11 +522,6 @@ pub struct InitialConstellationState { /// The XR device registry pub webxr_registry: Option, - pub glplayer_threads: Option, - - /// Application window's GL Context for Media player - pub player_context: WindowGLContext, - /// User agent string to report in network requests. pub user_agent: Cow<'static, str>, @@ -759,8 +748,6 @@ where pending_approval_navigations: HashMap::new(), pressed_mouse_buttons: 0, hard_fail, - glplayer_threads: state.glplayer_threads, - player_context: state.player_context, active_media_session: None, user_agent: state.user_agent, rippy_data, @@ -1011,7 +998,7 @@ where .as_ref() .map(|threads| threads.pipeline()), webxr_registry: self.webxr_registry.clone(), - player_context: self.player_context.clone(), + player_context: WindowGLContext::get(), user_agent: self.user_agent.clone(), rippy_data: self.rippy_data.clone(), }); @@ -2690,11 +2677,7 @@ 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); - } - } + WindowGLContext::get().exit(); debug!("Exiting the system font service thread."); self.system_font_service.exit(); diff --git a/components/media/Cargo.toml b/components/media/Cargo.toml index e9724ee9bf6..7fabf427d90 100644 --- a/components/media/Cargo.toml +++ b/components/media/Cargo.toml @@ -12,6 +12,7 @@ name = "media" path = "lib.rs" [dependencies] +servo_config = { path = "../config" } euclid = { workspace = true } fnv = { workspace = true } ipc-channel = { workspace = true } diff --git a/components/media/lib.rs b/components/media/lib.rs index f29493eb4b3..7de19cb6121 100644 --- a/components/media/lib.rs +++ b/components/media/lib.rs @@ -5,28 +5,36 @@ #![deny(unsafe_code)] #![allow(clippy::type_complexity)] -mod media_channel; mod media_thread; use std::sync::{Arc, Mutex}; use euclid::default::Size2D; +use ipc_channel::ipc::{channel, IpcReceiver, IpcSender}; +use log::warn; use serde::{Deserialize, Serialize}; +use servo_config::pref; pub use servo_media::player::context::{GlApi, GlContext, NativeDisplay, PlayerGLContext}; use webrender_traits::{ - WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageSource, + WebrenderExternalImageApi, WebrenderExternalImageHandlers, WebrenderExternalImageRegistry, + WebrenderImageHandlerType, WebrenderImageSource, }; -pub use crate::media_channel::glplayer_channel; -use crate::media_channel::{GLPlayerChan, GLPlayerPipeline, GLPlayerReceiver, GLPlayerSender}; use crate::media_thread::GLPlayerThread; +/// A global version of the [`WindowGLContext`] to be shared between the embedder and the +/// constellation. This is only okay to do because OpenGL contexts cannot be used across processes +/// anyway. +/// +/// This avoid having to establish a depenency on `media` in `*_traits` crates. +static WINDOW_GL_CONTEXT: Mutex = Mutex::new(WindowGLContext::inactive()); + /// 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)>), + Lock(IpcSender<(u32, Size2D, usize)>), Unlock(), } @@ -38,7 +46,7 @@ pub enum GLPlayerMsgForward { #[derive(Debug, Deserialize, Serialize)] pub enum GLPlayerMsg { /// Registers an instantiated player in DOM - RegisterPlayer(GLPlayerSender), + RegisterPlayer(IpcSender), /// Unregisters a player's ID UnregisterPlayer(u64), /// Locks a specific texture from a player. Lock messages are used @@ -53,7 +61,7 @@ pub enum GLPlayerMsg { /// /// Currently OpenGL Sync Objects are used to implement the /// synchronization mechanism. - Lock(u64, GLPlayerSender<(u32, Size2D, usize)>), + Lock(u64, IpcSender<(u32, Size2D, usize)>), /// Unlocks a specific texture from a player. Unlock messages are /// used for a correct synchronization with WebRender external /// image API. @@ -67,56 +75,117 @@ pub enum GLPlayerMsg { Exit, } +/// A [`PlayerGLContext`] that renders to a window. Note that if the background +/// thread is not started for this context, then it is inactive (returning +/// `Unknown` values in the trait implementation). #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WindowGLContext { /// Application's GL Context - pub gl_context: GlContext, + pub context: GlContext, /// Application's GL Api - pub gl_api: GlApi, + pub api: GlApi, /// Application's native display - pub native_display: NativeDisplay, + pub display: NativeDisplay, /// A channel to the GLPlayer thread. - pub glplayer_chan: Option, + pub glplayer_thread_sender: Option>, +} + +impl WindowGLContext { + /// Create an inactive [`WindowGLContext`]. + pub const fn inactive() -> Self { + WindowGLContext { + context: GlContext::Unknown, + api: GlApi::None, + display: NativeDisplay::Unknown, + glplayer_thread_sender: None, + } + } + + pub fn register(context: Self) { + *WINDOW_GL_CONTEXT.lock().unwrap() = context; + } + + pub fn get() -> Self { + WINDOW_GL_CONTEXT.lock().unwrap().clone() + } + + /// Sends an exit message to close the GLPlayerThread. + pub fn exit(&self) { + self.send(GLPlayerMsg::Exit); + } + + #[inline] + pub fn send(&self, message: GLPlayerMsg) { + // Don't do anything if GL accelerated playback is disabled. + let Some(sender) = self.glplayer_thread_sender.as_ref() else { + return; + }; + + if let Err(error) = sender.send(message) { + warn!("Could no longer communicate with GL accelerated media threads: {error}") + } + } + + pub fn initialize(display: NativeDisplay, api: GlApi, context: GlContext) { + if matches!(display, NativeDisplay::Unknown) || matches!(context, GlContext::Unknown) { + return; + } + + let mut window_gl_context = WINDOW_GL_CONTEXT.lock().unwrap(); + if window_gl_context.glplayer_thread_sender.is_some() { + warn!("Not going to initialize GL accelerated media playback more than once."); + return; + } + + window_gl_context.context = context; + window_gl_context.display = display; + window_gl_context.api = api; + } + + pub fn initialize_image_handler( + external_image_handlers: &mut WebrenderExternalImageHandlers, + external_images: Arc>, + ) { + if !pref!(media_glvideo_enabled) { + return; + } + + let mut window_gl_context = WINDOW_GL_CONTEXT.lock().unwrap(); + if window_gl_context.glplayer_thread_sender.is_some() { + warn!("Not going to initialize GL accelerated media playback more than once."); + return; + } + + if matches!(window_gl_context.display, NativeDisplay::Unknown) || + matches!(window_gl_context.context, GlContext::Unknown) + { + return; + } + + let thread_sender = GLPlayerThread::start(external_images); + let image_handler = Box::new(GLPlayerExternalImages::new(thread_sender.clone())); + external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::Media); + window_gl_context.glplayer_thread_sender = Some(thread_sender); + } } impl PlayerGLContext for WindowGLContext { fn get_gl_context(&self) -> GlContext { - self.gl_context.clone() + match self.glplayer_thread_sender { + Some(..) => self.context.clone(), + None => GlContext::Unknown, + } } fn get_native_display(&self) -> NativeDisplay { - self.native_display.clone() + match self.glplayer_thread_sender { + Some(..) => self.display.clone(), + None => NativeDisplay::Unknown, + } } 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") + self.api.clone() } } @@ -126,20 +195,20 @@ struct GLPlayerExternalImages { // @FIXME(victor): this should be added when GstGLSyncMeta is // added //webrender_gl: Rc, - glplayer_channel: GLPlayerSender, + glplayer_channel: IpcSender, // Used to avoid creating a new channel on each received WebRender // request. lock_channel: ( - GLPlayerSender<(u32, Size2D, usize)>, - GLPlayerReceiver<(u32, Size2D, usize)>, + IpcSender<(u32, Size2D, usize)>, + IpcReceiver<(u32, Size2D, usize)>, ), } impl GLPlayerExternalImages { - fn new(channel: GLPlayerSender) -> Self { + fn new(sender: IpcSender) -> Self { Self { - glplayer_channel: channel, - lock_channel: glplayer_channel().unwrap(), + glplayer_channel: sender, + lock_channel: channel().unwrap(), } } } diff --git a/components/media/media_channel/ipc.rs b/components/media/media_channel/ipc.rs deleted file mode 100644 index a66c697d382..00000000000 --- a/components/media/media_channel/ipc.rs +++ /dev/null @@ -1,15 +0,0 @@ -/* 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 std::io; - -use serde::{Deserialize, Serialize}; - -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 deleted file mode 100644 index 3468d67bbec..00000000000 --- a/components/media/media_channel/mod.rs +++ /dev/null @@ -1,113 +0,0 @@ -/* 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 std::fmt; - -use serde::{Deserialize, Serialize}; - -use crate::GLPlayerMsg; - -#[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(|_| ()), - } - } - - #[allow(clippy::wrong_self_convention)] // It is an alias to the underlying module - pub fn to_ipc_receiver(self) -> ipc_channel::ipc::IpcReceiver { - match self { - GLPlayerReceiver::Ipc(receiver) => receiver, - _ => unreachable!(), - } - } -} - -pub fn glplayer_channel() -> Option<(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))) - .ok() - } else { - mpsc::glplayer_channel() - .map(|(tx, rx)| (GLPlayerSender::Mpsc(tx), GLPlayerReceiver::Mpsc(rx))) - .ok() - } -} - -#[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 deleted file mode 100644 index 3d5fa61b10b..00000000000 --- a/components/media/media_channel/mpsc.rs +++ /dev/null @@ -1,57 +0,0 @@ -/* 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 std::sync::mpsc; - -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -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 index 9bd556d6b72..7780323345c 100644 --- a/components/media/media_thread.rs +++ b/components/media/media_thread.rs @@ -6,11 +6,11 @@ use std::sync::{Arc, Mutex}; use std::thread; use fnv::FnvHashMap; +use ipc_channel::ipc::{channel, IpcSender}; use log::{trace, warn}; use webrender_api::ExternalImageId; use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType}; -use crate::media_channel::{glplayer_channel, GLPlayerSender}; /// GL player threading API entry point that lives in the /// constellation. use crate::{GLPlayerMsg, GLPlayerMsgForward}; @@ -19,7 +19,7 @@ use crate::{GLPlayerMsg, GLPlayerMsgForward}; /// a set of video players with GL render. pub struct GLPlayerThread { /// Map of live players. - players: FnvHashMap>, + players: FnvHashMap>, /// List of registered webrender external images. /// We use it to get an unique ID for new players. external_images: Arc>, @@ -35,8 +35,8 @@ impl GLPlayerThread { pub fn start( external_images: Arc>, - ) -> GLPlayerSender { - let (sender, receiver) = glplayer_channel::().unwrap(); + ) -> IpcSender { + let (sender, receiver) = channel().unwrap(); thread::Builder::new() .name("GLPlayer".to_owned()) .spawn(move || { diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 395903a51ab..1ba1c53c66f 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -17,10 +17,10 @@ use headers::{ContentLength, ContentRange, HeaderMapExt}; use html5ever::{local_name, namespace_url, ns, LocalName, Prefix}; use http::header::{self, HeaderMap, HeaderValue}; use http::StatusCode; -use ipc_channel::ipc::{self, IpcSharedMemory}; +use ipc_channel::ipc::{self, channel, IpcSharedMemory}; use ipc_channel::router::ROUTER; use js::jsapi::JSAutoRealm; -use media::{glplayer_channel, GLPlayerMsg, GLPlayerMsgForward, WindowGLContext}; +use media::{GLPlayerMsg, GLPlayerMsgForward, WindowGLContext}; use net_traits::request::{Destination, RequestId}; use net_traits::{ FetchMetadata, FetchResponseListener, Metadata, NetworkError, ResourceFetchTiming, @@ -1399,12 +1399,10 @@ impl HTMLMediaElement { // GLPlayer thread setup let (player_id, image_receiver) = window .get_player_context() - .glplayer_chan + .glplayer_thread_sender .map(|pipeline| { - let (image_sender, image_receiver) = - glplayer_channel::().unwrap(); + let (image_sender, image_receiver) = channel().unwrap(); pipeline - .channel() .send(GLPlayerMsg::RegisterPlayer(image_sender)) .unwrap(); match image_receiver.recv().unwrap() { @@ -1425,7 +1423,7 @@ impl HTMLMediaElement { .media_element_task_source() .to_sendable(); ROUTER.add_typed_route( - image_receiver.to_ipc_receiver(), + image_receiver, Box::new(move |message| { let msg = message.unwrap(); let this = trusted_node.clone(); @@ -2051,14 +2049,8 @@ impl HTMLMediaElement { impl Drop for HTMLMediaElement { fn drop(&mut self) { - if let Some(ref pipeline) = self.player_context.glplayer_chan { - if let Err(err) = pipeline - .channel() - .send(GLPlayerMsg::UnregisterPlayer(self.id.get())) - { - warn!("GLPlayer disappeared!: {:?}", err); - } - } + self.player_context + .send(GLPlayerMsg::UnregisterPlayer(self.id.get())); } } diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index f2b9b802307..52a27d340cd 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -99,6 +99,7 @@ servo_geometry = { path = "../geometry" } servo_url = { path = "../url" } style = { workspace = true } style_traits = { workspace = true } +surfman = { workspace = true } tracing = { workspace = true, optional = true } url = { workspace = true } webdriver_server = { path = "../webdriver_server", optional = true } @@ -109,12 +110,10 @@ webrender_traits = { workspace = true } webxr-api = { workspace = true, optional = true } [target.'cfg(any(target_os = "android", target_env = "ohos"))'.dependencies] -surfman = { workspace = true, features = ["sm-angle-default"] } webxr = { path = "../webxr", optional = true } [target.'cfg(not(any(target_os = "android", target_env = "ohos")))'.dependencies] arboard = { workspace = true, optional = true } -surfman = { workspace = true, features = ["sm-x11", "sm-raw-window-handle-06"] } webxr = { path = "../webxr", features = ["ipc", "glwindow", "headless"] } [target.'cfg(all(not(target_os = "windows"), not(target_os = "ios"), not(target_os = "android"), not(target_env = "ohos"), not(target_arch = "arm"), not(target_arch = "aarch64")))'.dependencies] diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 791074bb558..c078b392fc4 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -80,7 +80,7 @@ pub use keyboard_types::*; #[cfg(feature = "layout_2013")] pub use layout_thread_2013; use log::{warn, Log, Metadata, Record}; -use media::{GLPlayerThreads, GlApi, NativeDisplay, WindowGLContext}; +use media::{GlApi, NativeDisplay, WindowGLContext}; use net::protocols::ProtocolRegistry; use net::resource_thread::new_resource_threads; use profile::{mem as profile_mem, time as profile_time}; @@ -101,7 +101,6 @@ pub use webgpu; use webgpu::swapchain::WGPUImageMap; use webrender::{RenderApiSender, ShaderPrecacheFlags, UploadMethod, ONE_TIME_USAGE_HINT}; use webrender_api::{ColorF, DocumentId, FramePublishId}; -use webrender_traits::rendering_context::GLVersion; pub use webrender_traits::rendering_context::{ OffscreenRenderingContext, RenderingContext, SoftwareRenderingContext, SurfmanRenderingContext, WindowRenderingContext, @@ -462,10 +461,9 @@ impl Servo { WebrenderImageHandlerType::WebGPU, ); - let (player_context, glplayer_threads) = Self::create_media_window_gl_context( + WindowGLContext::initialize_image_handler( &mut external_image_handlers, external_images.clone(), - &rendering_context, ); webrender.set_external_image_handler(external_image_handlers); @@ -494,9 +492,7 @@ impl Servo { webrender_api_sender, #[cfg(feature = "webxr")] webxr_main_thread.registry(), - player_context, Some(webgl_threads), - glplayer_threads, window_size, external_images, #[cfg(feature = "webgpu")] @@ -558,66 +554,10 @@ impl Servo { *self.delegate.borrow_mut() = delegate; } - fn create_media_window_gl_context( - external_image_handlers: &mut WebrenderExternalImageHandlers, - external_images: Arc>, - rendering_context: &Rc, - ) -> (WindowGLContext, Option) { - if !pref!(media_glvideo_enabled) { - return ( - WindowGLContext { - gl_context: GlContext::Unknown, - gl_api: GlApi::None, - native_display: NativeDisplay::Unknown, - glplayer_chan: None, - }, - None, - ); - } - - let native_display = rendering_context.gl_display(); - let gl_context = rendering_context.gl_context(); - if let (NativeDisplay::Unknown, GlContext::Unknown) = (&native_display, &gl_context) { - return ( - WindowGLContext { - gl_context: GlContext::Unknown, - gl_api: GlApi::None, - native_display: NativeDisplay::Unknown, - glplayer_chan: None, - }, - None, - ); - } - let gl_api = match rendering_context.gl_version() { - GLVersion::GL(major, minor) => { - if major >= 3 && minor >= 2 { - GlApi::OpenGL3 - } else { - GlApi::OpenGL - } - }, - GLVersion::GLES(major, _) => { - if major > 1 { - GlApi::Gles2 - } else { - GlApi::Gles1 - } - }, - }; - - assert!(!matches!(gl_context, GlContext::Unknown)); - let (glplayer_threads, image_handler) = GLPlayerThreads::new(external_images.clone()); - external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::Media); - - ( - WindowGLContext { - gl_context, - native_display, - gl_api, - glplayer_chan: Some(GLPlayerThreads::pipeline(&glplayer_threads)), - }, - Some(glplayer_threads), - ) + /// **EXPERIMENTAL:** Intialize GL accelerated media playback. This currently only works on a limited number + /// of platforms. This should be run *before* calling [`Servo::new`] and creating the first [`WebView`]. + pub fn initialize_gl_accelerated_media(display: NativeDisplay, api: GlApi, context: GlContext) { + WindowGLContext::initialize(display, api, context) } /// Spin the Servo event loop, which: @@ -1096,9 +1036,7 @@ fn create_constellation( webrender_document: DocumentId, webrender_api_sender: RenderApiSender, #[cfg(feature = "webxr")] webxr_registry: webxr_api::Registry, - player_context: WindowGLContext, webgl_threads: Option, - glplayer_threads: Option, initial_window_size: WindowSizeData, external_images: Arc>, #[cfg(feature = "webgpu")] wgpu_image_map: WGPUImageMap, @@ -1151,8 +1089,6 @@ fn create_constellation( #[cfg(not(feature = "webxr"))] webxr_registry: None, webgl_threads, - glplayer_threads, - player_context, user_agent, webrender_external_images: external_images, #[cfg(feature = "webgpu")] diff --git a/components/shared/webrender/rendering_context.rs b/components/shared/webrender/rendering_context.rs index 631b0b28acf..fca08b4d79b 100644 --- a/components/shared/webrender/rendering_context.rs +++ b/components/shared/webrender/rendering_context.rs @@ -4,7 +4,7 @@ #![deny(unsafe_code)] -use std::cell::{Cell, RefCell}; +use std::cell::{Cell, RefCell, RefMut}; use std::ffi::c_void; use std::num::NonZeroU32; use std::rc::Rc; @@ -17,24 +17,13 @@ use glow::NativeFramebuffer; use image::RgbaImage; use log::{debug, trace, warn}; use raw_window_handle::{DisplayHandle, WindowHandle}; -use servo_media::player::context::{GlContext, NativeDisplay}; use surfman::chains::{PreserveBuffer, SwapChain}; -#[cfg(all(target_os = "linux", not(target_env = "ohos")))] -use surfman::platform::generic::multi::connection::NativeConnection as LinuxNativeConnection; -#[cfg(all(target_os = "linux", not(target_env = "ohos")))] -use surfman::platform::generic::multi::context::NativeContext as LinuxNativeContext; pub use surfman::Error; use surfman::{ Adapter, Connection, Context, ContextAttributeFlags, ContextAttributes, Device, GLApi, NativeContext, NativeWidget, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType, }; -/// Describes the OpenGL version that is requested when a context is created. -pub enum GLVersion { - GL(u8, u8), - GLES(u8, u8), -} - /// The `RenderingContext` trait defines a set of methods for managing /// an OpenGL or GLES rendering context. /// Implementors of this trait are responsible for handling the creation, @@ -63,16 +52,6 @@ pub trait RenderingContext { fn make_current(&self) -> Result<(), Error>; /// Returns the OpenGL or GLES API. fn gl_api(&self) -> Rc; - /// Describes the OpenGL version that is requested when a context is created. - fn gl_version(&self) -> GLVersion; - /// Returns the GL Context used by servo media player. Default to `GlContext::Unknown`. - fn gl_context(&self) -> GlContext { - GlContext::Unknown - } - /// Returns the GL Display used by servo media player. Default to `NativeDisplay::Unknown`. - fn gl_display(&self) -> NativeDisplay { - NativeDisplay::Unknown - } /// Creates a texture from a given surface and returns the surface texture, /// the OpenGL texture object, and the size of the surface. Default to `None`. fn create_texture(&self, _surface: Surface) -> Option<(SurfaceTexture, u32, Size2D)> { @@ -215,82 +194,6 @@ impl SurfmanRenderingContext { } impl RenderingContext for SurfmanRenderingContext { - fn gl_context(&self) -> GlContext { - #[cfg(all(target_os = "linux", not(target_env = "ohos")))] - { - match self.native_context() { - NativeContext::Default(LinuxNativeContext::Default(native_context)) => { - GlContext::Egl(native_context.egl_context as usize) - }, - NativeContext::Default(LinuxNativeContext::Alternate(native_context)) => { - GlContext::Egl(native_context.egl_context as usize) - }, - NativeContext::Alternate(_) => GlContext::Unknown, - } - } - #[cfg(target_os = "windows")] - { - #[cfg(feature = "no-wgl")] - { - GlContext::Egl(self.native_context().egl_context as usize) - } - #[cfg(not(feature = "no-wgl"))] - GlContext::Unknown - } - #[cfg(not(any( - target_os = "windows", - all(target_os = "linux", not(target_env = "ohos")) - )))] - { - GlContext::Unknown - } - } - - fn gl_display(&self) -> NativeDisplay { - #[cfg(all(target_os = "linux", not(target_env = "ohos")))] - { - match self.device.borrow().connection().native_connection() { - surfman::NativeConnection::Default(LinuxNativeConnection::Default(connection)) => { - NativeDisplay::Egl(connection.0 as usize) - }, - surfman::NativeConnection::Default(LinuxNativeConnection::Alternate( - connection, - )) => NativeDisplay::X11(connection.x11_display as usize), - surfman::NativeConnection::Alternate(_) => NativeDisplay::Unknown, - } - } - #[cfg(target_os = "windows")] - { - #[cfg(feature = "no-wgl")] - { - let device = &self.device.borrow(); - NativeDisplay::Egl(device.native_device().egl_display as usize) - } - #[cfg(not(feature = "no-wgl"))] - NativeDisplay::Unknown - } - #[cfg(not(any( - target_os = "windows", - all(target_os = "linux", not(target_env = "ohos")) - )))] - { - NativeDisplay::Unknown - } - } - - fn gl_version(&self) -> GLVersion { - let device = self.device.borrow(); - let context = self.context.borrow(); - let descriptor = device.context_descriptor(&context); - let attributes = device.context_descriptor_attributes(&descriptor); - let major = attributes.version.major; - let minor = attributes.version.minor; - match device.connection().gl_api() { - GLApi::GL => GLVersion::GL(major, minor), - GLApi::GLES => GLVersion::GLES(major, minor), - } - } - fn gl_api(&self) -> Rc { self.gl.clone() } @@ -397,14 +300,6 @@ impl Drop for SoftwareRenderingContext { } impl RenderingContext for SoftwareRenderingContext { - fn gl_context(&self) -> GlContext { - self.surfman_rendering_info.gl_context() - } - - fn gl_display(&self) -> NativeDisplay { - self.surfman_rendering_info.gl_display() - } - fn prepare_for_rendering(&self) { self.surfman_rendering_info.prepare_for_rendering(); } @@ -436,10 +331,6 @@ impl RenderingContext for SoftwareRenderingContext { self.surfman_rendering_info.gl.clone() } - fn gl_version(&self) -> GLVersion { - self.surfman_rendering_info.gl_version() - } - fn create_texture(&self, surface: Surface) -> Option<(SurfaceTexture, u32, Size2D)> { self.surfman_rendering_info.create_texture(surface) } @@ -544,17 +435,13 @@ impl WindowRenderingContext { device.make_context_current(&context)?; Ok(()) } + + pub fn surfman_details(&self) -> (RefMut, RefMut) { + (self.0.device.borrow_mut(), self.0.context.borrow_mut()) + } } impl RenderingContext for WindowRenderingContext { - fn gl_context(&self) -> GlContext { - self.0.gl_context() - } - - fn gl_display(&self) -> NativeDisplay { - self.0.gl_display() - } - fn prepare_for_rendering(&self) { self.0.prepare_for_rendering(); } @@ -584,10 +471,6 @@ impl RenderingContext for WindowRenderingContext { self.0.gl.clone() } - fn gl_version(&self) -> GLVersion { - self.0.gl_version() - } - fn create_texture(&self, surface: Surface) -> Option<(SurfaceTexture, u32, Size2D)> { self.0.create_texture(surface) } @@ -873,10 +756,6 @@ impl RenderingContext for OffscreenRenderingContext { self.parent_context.gl_api() } - fn gl_version(&self) -> GLVersion { - self.parent_context.gl_version() - } - fn create_texture(&self, surface: Surface) -> Option<(SurfaceTexture, u32, Size2D)> { self.parent_context.create_texture(surface) } diff --git a/ports/servoshell/desktop/accelerated_gl_media.rs b/ports/servoshell/desktop/accelerated_gl_media.rs new file mode 100644 index 00000000000..1ee9011a48c --- /dev/null +++ b/ports/servoshell/desktop/accelerated_gl_media.rs @@ -0,0 +1,73 @@ +/* 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 std::cell::RefMut; + +use surfman::{Context, Device}; + +#[cfg(not(any( + target_os = "windows", + all(target_os = "linux", not(target_env = "ohos")) +)))] +pub(crate) fn setup_gl_accelerated_media(_: RefMut, _: RefMut) {} + +#[cfg(all(target_os = "linux", not(target_env = "ohos")))] +pub(crate) fn setup_gl_accelerated_media(device: RefMut, context: RefMut) { + use servo::media::{GlContext, NativeDisplay}; + use servo::Servo; + use surfman::platform::generic::multi::connection::NativeConnection; + use surfman::platform::generic::multi::context::NativeContext; + + let api = api(&device, &context); + let context = match device.native_context(&context) { + NativeContext::Default(NativeContext::Default(native_context)) => { + GlContext::Egl(native_context.egl_context as usize) + }, + NativeContext::Default(NativeContext::Alternate(native_context)) => { + GlContext::Egl(native_context.egl_context as usize) + }, + NativeContext::Alternate(_) => GlContext::Unknown, + }; + let display = match device.connection().native_connection() { + surfman::NativeConnection::Default(NativeConnection::Default(connection)) => { + NativeDisplay::Egl(connection.0 as usize) + }, + surfman::NativeConnection::Default(NativeConnection::Alternate(connection)) => { + NativeDisplay::X11(connection.x11_display as usize) + }, + surfman::NativeConnection::Alternate(_) => NativeDisplay::Unknown, + }; + Servo::initialize_gl_accelerated_media(display, api, context); +} + +#[cfg(target_os = "windows")] +pub(crate) fn setup_gl_accelerated_media(device: RefMut, context: RefMut) { + use servo::media::{GlContext, NativeDisplay}; + use servo::Servo; + + let api = api(&device, &context); + let context = GlContext::Egl(device.native_context(&context).egl_context as usize); + let display = NativeDisplay::Egl(device.native_device().egl_display as usize); + Servo::initialize_gl_accelerated_media(display, api, context); +} + +#[cfg(any( + all(target_os = "linux", not(target_env = "ohos")), + target_os = "windows" +))] +fn api(device: &RefMut, context: &RefMut) -> servo::media::GlApi { + use servo::media::GlApi; + use surfman::GLApi; + + let descriptor = device.context_descriptor(context); + let attributes = device.context_descriptor_attributes(&descriptor); + let major = attributes.version.major; + let minor = attributes.version.minor; + match device.connection().gl_api() { + GLApi::GL if major >= 3 && minor >= 2 => GlApi::OpenGL3, + GLApi::GL => GlApi::OpenGL, + GLApi::GLES if major > 1 => GlApi::Gles2, + GLApi::GLES => GlApi::Gles1, + } +} diff --git a/ports/servoshell/desktop/headed_window.rs b/ports/servoshell/desktop/headed_window.rs index 784aa4fa7c7..58316ad1aae 100644 --- a/ports/servoshell/desktop/headed_window.rs +++ b/ports/servoshell/desktop/headed_window.rs @@ -43,6 +43,7 @@ use super::app_state::RunningAppState; use super::geometry::{winit_position_to_euclid_point, winit_size_to_euclid_size}; use super::keyutils::{keyboard_event_from_winit, CMD_OR_ALT}; use super::window_trait::{WindowPortsMethods, LINE_HEIGHT}; +use crate::desktop::accelerated_gl_media::setup_gl_accelerated_media; use crate::desktop::keyutils::CMD_OR_CONTROL; use crate::prefs::ServoShellPreferences; @@ -132,6 +133,13 @@ impl Window { .expect("Could not create RenderingContext for Window"), ); + // Setup for GL accelerated media handling. This is only active on certain Linux platforms + // and Windows. + { + let details = window_rendering_context.surfman_details(); + setup_gl_accelerated_media(details.0, details.1); + } + // Make sure the gl context is made current. window_rendering_context.make_current().unwrap(); diff --git a/ports/servoshell/desktop/mod.rs b/ports/servoshell/desktop/mod.rs index c6c78f2f799..ffa1353906b 100644 --- a/ports/servoshell/desktop/mod.rs +++ b/ports/servoshell/desktop/mod.rs @@ -4,6 +4,7 @@ //! Contains files specific to the servoshell app for Desktop systems. +mod accelerated_gl_media; pub(crate) mod app; mod app_state; pub(crate) mod cli;