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:
bors-servo 2019-12-20 16:09:16 -05:00 committed by GitHub
commit 62899c0f52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 316 additions and 133 deletions

56
Cargo.lock generated
View file

@ -478,8 +478,8 @@ dependencies = [
"raqote", "raqote",
"servo_config", "servo_config",
"sparkle", "sparkle",
"surfman", "surfman 0.1.3",
"surfman-chains", "surfman-chains 0.2.1",
"surfman-chains-api", "surfman-chains-api",
"webrender", "webrender",
"webrender_api", "webrender_api",
@ -3000,7 +3000,7 @@ dependencies = [
"sparkle", "sparkle",
"style", "style",
"style_traits", "style_traits",
"surfman", "surfman 0.1.3",
"webdriver_server", "webdriver_server",
"webgpu", "webgpu",
"webrender", "webrender",
@ -4816,8 +4816,8 @@ dependencies = [
"log", "log",
"servo-media", "servo-media",
"sparkle", "sparkle",
"surfman", "surfman 0.2.0",
"surfman-chains", "surfman-chains 0.3.0",
"surfman-chains-api", "surfman-chains-api",
] ]
@ -5500,6 +5500,33 @@ dependencies = [
"x11", "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]] [[package]]
name = "surfman-chains" name = "surfman-chains"
version = "0.2.1" version = "0.2.1"
@ -5510,7 +5537,20 @@ dependencies = [
"fnv", "fnv",
"log", "log",
"sparkle", "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", "surfman-chains-api",
] ]
@ -6420,8 +6460,8 @@ dependencies = [
"log", "log",
"openxr", "openxr",
"serde", "serde",
"surfman", "surfman 0.1.3",
"surfman-chains", "surfman-chains 0.2.1",
"time", "time",
"webxr-api", "webxr-api",
"winapi", "winapi",

View file

@ -491,9 +491,9 @@ def windows_unit(cached=True):
"mach smoketest --angle", "mach smoketest --angle",
"mach package --dev", "mach package --dev",
"mach build --dev --libsimpleservo", "mach build --dev --libsimpleservo",
# We're getting link errors on windows, due to the x11 feature being # The GStreamer plugin currently doesn't support Windows
# enabled on gstreamer-gl. https://github.com/servo/media/pull/304/ # https://github.com/servo/servo/issues/25353
"mach build --dev --media-stack=dummy -p servo-gst-plugin", # "mach build --dev -p servo-gst-plugin",
) )
.with_artifacts("repo/target/debug/msi/Servo.exe", .with_artifacts("repo/target/debug/msi/Servo.exe",

View file

@ -22,7 +22,7 @@ glib = { version = "0.8", features = ["subclassing"] }
gstreamer = { version = "0.14", features = ["subclassing"] } gstreamer = { version = "0.14", features = ["subclassing"] }
gstreamer-base = { version = "0.14", features = ["subclassing"] } gstreamer-base = { version = "0.14", features = ["subclassing"] }
gstreamer-gl = { version = "0.14" } 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-sys = { version = "0.8" }
gstreamer-video = { version = "0.14", features = ["subclassing"] } gstreamer-video = { version = "0.14", features = ["subclassing"] }
log = "0.4" log = "0.4"
@ -30,10 +30,9 @@ lazy_static = "1.4"
libservo = {path = "../../components/servo"} libservo = {path = "../../components/servo"}
servo-media = {git = "https://github.com/servo/media"} servo-media = {git = "https://github.com/servo/media"}
sparkle = "0.1" sparkle = "0.1"
# NOTE: the sm-angle-default feature only enables angle on windows, not other platforms! surfman = { git = "https://github.com/pcwalton/surfman", branch = "multi" }
surfman = { version = "0.1", features = ["sm-angle-default", "sm-osmesa"] }
surfman-chains-api = "0.2" surfman-chains-api = "0.2"
surfman-chains = "0.2.1" surfman-chains = { git = "https://github.com/asajeffrey/surfman-chains", branch = "multi" }
[build-dependencies] [build-dependencies]
gst-plugin-version-helper = "0.1" gst-plugin-version-helper = "0.1"

View file

@ -1,5 +1,10 @@
# A GStreamer plugin which runs servo # A GStreamer plugin which runs servo
## Supported platforms
* MacOS + CGL
* Linux + Wayland (currently no WebGL content)
## Build ## Build
``` ```
@ -68,8 +73,6 @@ LD_LIBRARY_PATH=$PWD/support/linux/gstreamer/gst/lib \
## Troubleshooting running the plugin ## Troubleshooting running the plugin
*Currently x11 support is broken!*
First try: First try:
``` ```
GST_PLUGIN_PATH=target/gstplugins \ GST_PLUGIN_PATH=target/gstplugins \

View file

@ -9,7 +9,7 @@ use lazy_static::lazy_static;
lazy_static! { lazy_static! {
pub static ref CATEGORY: DebugCategory = pub static ref CATEGORY: DebugCategory =
DebugCategory::new("servosrc", DebugColorFlags::empty(), Some("Servo")); DebugCategory::new("servowebsrc", DebugColorFlags::empty(), Some("Servo"));
} }
pub static LOGGER: ServoSrcLogger = ServoSrcLogger; pub static LOGGER: ServoSrcLogger = ServoSrcLogger;

View file

@ -25,6 +25,7 @@ use glib::subclass::object::Property;
use glib::subclass::simple::ClassStruct; use glib::subclass::simple::ClassStruct;
use glib::subclass::types::ObjectSubclass; use glib::subclass::types::ObjectSubclass;
use glib::translate::FromGlibPtrBorrow; use glib::translate::FromGlibPtrBorrow;
use glib::translate::ToGlibPtr;
use glib::value::Value; use glib::value::Value;
use glib::ParamSpec; use glib::ParamSpec;
use gstreamer::gst_element_error; use gstreamer::gst_element_error;
@ -55,6 +56,7 @@ use gstreamer_base::BaseSrcExt;
use gstreamer_gl::GLContext; use gstreamer_gl::GLContext;
use gstreamer_gl::GLContextExt; use gstreamer_gl::GLContextExt;
use gstreamer_gl::GLContextExtManual; use gstreamer_gl::GLContextExtManual;
use gstreamer_gl::GLSyncMeta;
use gstreamer_gl_sys::gst_gl_context_thread_add; use gstreamer_gl_sys::gst_gl_context_thread_add;
use gstreamer_gl_sys::gst_gl_texture_target_to_gl; use gstreamer_gl_sys::gst_gl_texture_target_to_gl;
use gstreamer_gl_sys::gst_is_gl_memory; use gstreamer_gl_sys::gst_is_gl_memory;
@ -63,6 +65,7 @@ use gstreamer_gl_sys::GstGLMemory;
use gstreamer_video::VideoInfo; use gstreamer_video::VideoInfo;
use log::debug; use log::debug;
use log::error;
use log::info; use log::info;
use servo::compositing::windowing::AnimationState; use servo::compositing::windowing::AnimationState;
@ -80,14 +83,28 @@ use sparkle::gl;
use sparkle::gl::types::GLuint; use sparkle::gl::types::GLuint;
use sparkle::gl::Gl; use sparkle::gl::Gl;
use surfman::platform::generic::universal::context::Context; use surfman::connection::Connection as ConnectionAPI;
use surfman::platform::generic::universal::device::Device; use surfman::device::Device as DeviceAPI;
use surfman::ContextAttributeFlags;
use surfman::ContextAttributes;
use surfman::GLApi;
use surfman::GLVersion;
use surfman::SurfaceAccess; use surfman::SurfaceAccess;
use surfman::SurfaceType; use surfman::SurfaceType;
use surfman_chains::SwapChain; use surfman_chains::SwapChain;
use surfman_chains_api::SwapChainAPI; 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::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -102,10 +119,11 @@ use std::time::Instant;
pub struct ServoWebSrc { pub struct ServoWebSrc {
sender: Sender<ServoWebSrcMsg>, sender: Sender<ServoWebSrcMsg>,
swap_chain: SwapChain,
url: Mutex<Option<String>>, url: Mutex<Option<String>>,
info: Mutex<Option<VideoInfo>>, info: Mutex<Option<VideoInfo>>,
buffer_pool: Mutex<Option<BufferPool>>, buffer_pool: Mutex<Option<BufferPool>>,
gl_context: Mutex<Option<GLContext>>,
connection: Mutex<Option<Connection>>,
// When did the plugin get created? // When did the plugin get created?
start: Instant, start: Instant,
// How long should each frame last? // How long should each frame last?
@ -119,6 +137,7 @@ pub struct ServoWebSrc {
struct ServoWebSrcGfx { struct ServoWebSrcGfx {
device: Device, device: Device,
context: Context, context: Context,
swap_chain: SwapChain<Device>,
gl: Rc<Gl>, gl: Rc<Gl>,
read_fbo: GLuint, read_fbo: GLuint,
draw_fbo: GLuint, draw_fbo: GLuint,
@ -135,10 +154,18 @@ thread_local! {
static GFX_CACHE: RefCell<HashMap<GLContext, ServoWebSrcGfx>> = RefCell::new(HashMap::new()); 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)] #[derive(Debug)]
enum ServoWebSrcMsg { enum ServoWebSrcMsg {
Start(ServoUrl), Start(ConnectionWhichImplementsDebug, GLVersion, ServoUrl),
GetSwapChain(Sender<SwapChain>), GetSwapChain(Sender<SwapChain<Device>>),
Resize(Size2D<i32, DevicePixel>), Resize(Size2D<i32, DevicePixel>),
Heartbeat, Heartbeat,
Stop, Stop,
@ -152,18 +179,35 @@ const DEFAULT_FRAME_DURATION: Duration = Duration::from_micros(16_667);
struct ServoThread { struct ServoThread {
receiver: Receiver<ServoWebSrcMsg>, receiver: Receiver<ServoWebSrcMsg>,
swap_chain: SwapChain, swap_chain: SwapChain<Device>,
gfx: Rc<RefCell<ServoWebSrcGfx>>, gfx: Rc<RefCell<ServoThreadGfx>>,
servo: Servo<ServoWebSrcWindow>, servo: Servo<ServoWebSrcWindow>,
} }
struct ServoThreadGfx {
device: Device,
context: Context,
gl: Rc<Gl>,
}
impl ServoThread { impl ServoThread {
fn new(receiver: Receiver<ServoWebSrcMsg>) -> Self { 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 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 swap_chain = window.swap_chain.clone();
let gfx = window.gfx.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 { Self {
receiver, receiver,
swap_chain, swap_chain,
@ -176,7 +220,7 @@ impl ServoThread {
while let Ok(msg) = self.receiver.recv() { while let Ok(msg) = self.receiver.recv() {
debug!("Servo thread handling message {:?}", msg); debug!("Servo thread handling message {:?}", msg);
match msg { match msg {
ServoWebSrcMsg::Start(url) => self.new_browser(url), ServoWebSrcMsg::Start(..) => error!("Already started"),
ServoWebSrcMsg::GetSwapChain(sender) => sender ServoWebSrcMsg::GetSwapChain(sender) => sender
.send(self.swap_chain.clone()) .send(self.swap_chain.clone())
.expect("Failed to send swap chain"), .expect("Failed to send swap chain"),
@ -188,12 +232,6 @@ impl ServoThread {
self.servo.handle_events(vec![WindowEvent::Quit]); 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>) { fn resize(&mut self, size: Size2D<i32, DevicePixel>) {
{ {
let mut gfx = self.gfx.borrow_mut(); let mut gfx = self.gfx.borrow_mut();
@ -251,53 +289,57 @@ impl EventLoopWaker for ServoWebSrcEmbedder {
} }
struct ServoWebSrcWindow { struct ServoWebSrcWindow {
swap_chain: SwapChain, swap_chain: SwapChain<Device>,
gfx: Rc<RefCell<ServoWebSrcGfx>>, gfx: Rc<RefCell<ServoThreadGfx>>,
gl: Rc<dyn gleam::gl::Gl>, gl: Rc<dyn gleam::gl::Gl>,
} }
impl ServoWebSrcWindow { impl ServoWebSrcWindow {
fn new() -> Self { fn new(connection: Connection, version: GLVersion) -> Self {
let version = surfman::GLVersion { major: 4, minor: 3 }; let flags = ContextAttributeFlags::DEPTH |
let flags = surfman::ContextAttributeFlags::empty(); ContextAttributeFlags::STENCIL |
let attributes = surfman::ContextAttributes { version, flags }; ContextAttributeFlags::ALPHA;
let attributes = ContextAttributes { version, flags };
let connection = surfman::Connection::new().expect("Failed to create connection"); let adapter = connection
let adapter = surfman::Adapter::default().expect("Failed to create adapter"); .create_adapter()
let mut device = .expect("Failed to create adapter");
surfman::Device::new(&connection, &adapter).expect("Failed to create device"); let mut device = connection
.create_device(&adapter)
.expect("Failed to create device");
let descriptor = device let descriptor = device
.create_context_descriptor(&attributes) .create_context_descriptor(&attributes)
.expect("Failed to create descriptor"); .expect("Failed to create descriptor");
let context = device let mut context = device
.create_context(&descriptor) .create_context(&descriptor)
.expect("Failed to create context"); .expect("Failed to create context");
// This is a workaround for surfman having a different bootstrap API with Angle let (gleam, gl) = unsafe {
#[cfg(target_os = "windows")] match device.gl_api() {
let mut device = device; GLApi::GL => (
#[cfg(not(target_os = "windows"))] gleam::gl::GlFns::load_with(|s| device.get_proc_address(&context, s)),
let mut device = Device::Hardware(device); Gl::gl_fns(gl::ffi_gl::Gl::load_with(|s| {
#[cfg(target_os = "windows")] device.get_proc_address(&context, s)
let mut context = context; })),
#[cfg(not(target_os = "windows"))] ),
let mut context = Context::Hardware(context); GLApi::GLES => (
gleam::gl::GlesFns::load_with(|s| device.get_proc_address(&context, s)),
let gleam = Gl::gles_fns(gl::ffi_gles::Gles2::load_with(|s| {
unsafe { gleam::gl::GlFns::load_with(|s| device.get_proc_address(&context, 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) ),
})); }
};
device device
.make_context_current(&mut context) .make_context_current(&mut context)
.expect("Failed to make context current"); .expect("Failed to make context current");
debug_assert_eq!(gl.get_error(), gl::NO_ERROR); debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
let access = SurfaceAccess::GPUCPU; let access = SurfaceAccess::GPUOnly;
let size = Size2D::new(512, 512); let size = Size2D::new(512, 512);
let surface_type = SurfaceType::Generic { size }; let surface_type = SurfaceType::Generic { size };
let surface = device let surface = device
.create_surface(&mut context, access, &surface_type) .create_surface(&mut context, access, surface_type)
.expect("Failed to create surface"); .expect("Failed to create surface");
device device
@ -310,6 +352,8 @@ impl ServoWebSrcWindow {
.framebuffer_object; .framebuffer_object;
gl.viewport(0, 0, size.width, size.height); gl.viewport(0, 0, size.width, size.height);
gl.bind_framebuffer(gl::FRAMEBUFFER, fbo); 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!( debug_assert_eq!(
(gl.check_framebuffer_status(gl::FRAMEBUFFER), gl.get_error()), (gl.check_framebuffer_status(gl::FRAMEBUFFER), gl.get_error()),
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR) (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
@ -318,17 +362,12 @@ impl ServoWebSrcWindow {
let swap_chain = SwapChain::create_attached(&mut device, &mut context, access) let swap_chain = SwapChain::create_attached(&mut device, &mut context, access)
.expect("Failed to create swap chain"); .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(); device.make_no_context_current().unwrap();
let gfx = Rc::new(RefCell::new(ServoWebSrcGfx { let gfx = Rc::new(RefCell::new(ServoThreadGfx {
device, device,
context, context,
gl, gl,
read_fbo,
draw_fbo,
})); }));
Self { Self {
@ -354,9 +393,9 @@ impl WindowMethods for ServoWebSrcWindow {
), ),
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR) (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
); );
let _ = self self.swap_chain
.swap_chain .swap_buffers(&mut gfx.device, &mut gfx.context)
.swap_buffers(&mut gfx.device, &mut gfx.context); .expect("Failed to swap buffers");
let fbo = gfx let fbo = gfx
.device .device
.context_surface_info(&gfx.context) .context_surface_info(&gfx.context)
@ -381,6 +420,7 @@ impl WindowMethods for ServoWebSrcWindow {
gfx.device gfx.device
.make_context_current(&mut gfx.context) .make_context_current(&mut gfx.context)
.expect("Failed to make context current"); .expect("Failed to make context current");
debug!("EMBEDDER done make_context_current");
debug_assert_eq!( debug_assert_eq!(
( (
gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER), gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
@ -450,21 +490,21 @@ impl ObjectSubclass for ServoWebSrc {
fn new() -> Self { fn new() -> Self {
let (sender, receiver) = crossbeam_channel::bounded(1); let (sender, receiver) = crossbeam_channel::bounded(1);
thread::spawn(move || ServoThread::new(receiver).run()); 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 info = Mutex::new(None);
let url = Mutex::new(None); let url = Mutex::new(None);
let buffer_pool = 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 start = Instant::now();
let frame_duration_micros = AtomicU64::new(DEFAULT_FRAME_DURATION.as_micros() as u64); let frame_duration_micros = AtomicU64::new(DEFAULT_FRAME_DURATION.as_micros() as u64);
let next_frame_micros = AtomicU64::new(0); let next_frame_micros = AtomicU64::new(0);
Self { Self {
sender, sender,
swap_chain,
info, info,
url, url,
buffer_pool, buffer_pool,
gl_context,
connection,
start, start,
frame_duration_micros, frame_duration_micros,
next_frame_micros, next_frame_micros,
@ -527,7 +567,9 @@ impl ObjectImpl for ServoWebSrc {
impl ElementImpl for ServoWebSrc {} impl ElementImpl for ServoWebSrc {}
impl BaseSrcImpl 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 // Save the video info for later use
let info = VideoInfo::from_caps(outcaps) let info = VideoInfo::from_caps(outcaps)
.ok_or_else(|| gst_loggable_error!(CATEGORY, "Failed to get video info"))?; .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); .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 // 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 = let gst_gl_buffer_pool =
unsafe { gstreamer_gl_sys::gst_gl_buffer_pool_new(gst_gl_context) }; unsafe { gstreamer_gl_sys::gst_gl_buffer_pool_new(gst_gl_context) };
if gst_gl_buffer_pool.is_null() { if gst_gl_buffer_pool.is_null() {
@ -591,16 +627,63 @@ impl BaseSrcImpl for ServoWebSrc {
false false
} }
fn start(&self, _src: &BaseSrc) -> Result<(), ErrorMessage> { fn start(&self, src: &BaseSrc) -> Result<(), ErrorMessage> {
info!("Starting"); info!("Starting");
let guard = self
// Get the URL
let url_guard = self
.url .url
.lock() .lock()
.map_err(|_| gst_error_msg!(ResourceError::Settings, ["Failed to lock mutex"]))?; .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_string = url_guard.as_ref().map(|s| &**s).unwrap_or(DEFAULT_URL);
let url = ServoUrl::parse(url) let url = ServoUrl::parse(url_string)
.map_err(|_| gst_error_msg!(ResourceError::Settings, ["Failed to parse url"]))?; .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(()) Ok(())
} }
@ -634,10 +717,12 @@ impl BaseSrcImpl for ServoWebSrc {
// Activate the pool if necessary // Activate the pool if necessary
if !pool.is_active() { if !pool.is_active() {
debug!("Activating the buffer pool");
pool.set_active(true).map_err(|_| FlowError::Error)?; pool.set_active(true).map_err(|_| FlowError::Error)?;
} }
// Get a buffer to fill // Get a buffer to fill
debug!("Acquiring a buffer");
let buffer = pool.acquire_buffer(None)?; let buffer = pool.acquire_buffer(None)?;
// Get the GL memory from the buffer // Get the GL memory from the buffer
@ -657,23 +742,55 @@ impl BaseSrcImpl for ServoWebSrc {
// Fill the buffer on the GL thread // Fill the buffer on the GL thread
let result = Err(FlowError::Error); let result = Err(FlowError::Error);
let mut task = RunOnGLThread { let mut task = FillOnGLThread {
servo_web_src: self, servo_web_src: self,
src, src,
gl_memory, gl_memory,
result, 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) }; unsafe { gst_gl_context_thread_add(gl_memory.mem.context, Some(fill_on_gl_thread), data) };
task.result?; 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 // Wake up Servo
let _ = self.sender.send(ServoWebSrcMsg::Heartbeat); let _ = self.sender.send(ServoWebSrcMsg::Heartbeat);
Ok(buffer) 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, servo_web_src: &'a ServoWebSrc,
src: &'a BaseSrc, src: &'a BaseSrc,
gl_memory: &'a GstGLMemory, 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) { 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); let gl_context = GLContext::from_glib_borrow(context);
task.result = task task.result = task
.servo_web_src .servo_web_src
@ -713,32 +830,51 @@ impl ServoWebSrc {
let mut gfx_cache = gfx_cache.borrow_mut(); let mut gfx_cache = gfx_cache.borrow_mut();
let gfx = gfx_cache.entry(gl_context.clone()).or_insert_with(|| { let gfx = gfx_cache.entry(gl_context.clone()).or_insert_with(|| {
debug!("Bootstrapping surfman"); debug!("Bootstrapping surfman");
let (device, context) = unsafe { surfman::Device::from_current_context() } let connection_guard = self.connection.lock().unwrap();
.expect("Failed to bootstrap surfman"); let connection = connection_guard.as_ref().expect("Failed to get surfman");
let adapter = connection
// This is a workaround for surfman having a different bootstrap API with Angle .create_adapter()
#[cfg(not(target_os = "windows"))] .expect("Failed to bootstrap surfman adapter");
let device = Device::Hardware(device); let device = connection
#[cfg(not(target_os = "windows"))] .create_device(&adapter)
let context = Context::Hardware(context); .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| { let gl = Gl::gl_fns(gl::ffi_gl::Gl::load_with(|s| {
gl_context.get_proc_address(s) as *const _ gl_context.get_proc_address(s) as *const _
})); }));
let draw_fbo = gl.gen_framebuffers(1)[0]; let draw_fbo = gl.gen_framebuffers(1)[0];
let read_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 { ServoWebSrcGfx {
device, device,
context, context,
swap_chain,
gl, gl,
read_fbo, read_fbo,
draw_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); debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR);
// Save the current GL state // Save the current GL state
debug!("Saving the GL context");
let mut bound_fbos = [0, 0]; let mut bound_fbos = [0, 0];
unsafe { unsafe {
gfx.gl gfx.gl
@ -751,17 +887,18 @@ impl ServoWebSrc {
gfx.gl.framebuffer_texture_2d( gfx.gl.framebuffer_texture_2d(
gl::FRAMEBUFFER, gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0, gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D, draw_texture_target,
draw_texture_id, draw_texture_id,
0, 0,
); );
debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR); 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); gfx.gl.clear(gl::COLOR_BUFFER_BIT);
debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR); 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); let surface_size = Size2D::from_untyped(gfx.device.surface_info(&surface).size);
if size != surface_size { if size != surface_size {
// If we're being asked to fill frames that are a different size than servo is providing, // 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)); 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 let surface_texture = gfx
.device .device
.create_surface_texture(&mut gfx.context, surface) .create_surface_texture(&mut gfx.context, surface)
.unwrap(); .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(); 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.bind_framebuffer(gl::READ_FRAMEBUFFER, gfx.read_fbo);
gfx.gl.framebuffer_texture_2d( gfx.gl.framebuffer_texture_2d(
gl::READ_FRAMEBUFFER, gl::READ_FRAMEBUFFER,
@ -794,26 +941,18 @@ impl ServoWebSrc {
); );
debug_assert_eq!( 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() 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), gl::FRAMEBUFFER_COMPLETE,
gfx.gl.get_error() gl::FRAMEBUFFER_COMPLETE,
), gl::NO_ERROR
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR) )
); );
debug!( debug!("Blitting");
"Filling with {}/{} {}",
read_texture_id, read_texture_target, surface_size
);
gfx.gl.blit_framebuffer( gfx.gl.blit_framebuffer(
0, 0,
0, 0,
@ -838,7 +977,7 @@ impl ServoWebSrc {
.device .device
.destroy_surface_texture(&mut gfx.context, surface_texture) .destroy_surface_texture(&mut gfx.context, surface_texture)
.unwrap(); .unwrap();
self.swap_chain.recycle_surface(surface); gfx.swap_chain.recycle_surface(surface);
} else { } else {
debug!("Failed to get current surface"); debug!("Failed to get current surface");
} }
@ -851,11 +990,8 @@ impl ServoWebSrc {
debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR); 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(()) Ok(())
} }
} }
// TODO: Implement that trait for more platforms

View file

@ -44,6 +44,11 @@ packages = [
# https://github.com/servo/servo/issues/24421 # https://github.com/servo/servo/issues/24421
"proc-macro2", "proc-macro2",
"quote", "quote",
# These can be removed once servo is updated to surfman 0.2
"surfman",
"surfman-chains",
"syn", "syn",
"smallvec", "smallvec",
"unicode-xid", "unicode-xid",