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