aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--etc/taskcluster/decision_task.py4
-rw-r--r--ports/gstplugin/Cargo.toml3
-rw-r--r--ports/gstplugin/README.md21
-rw-r--r--ports/gstplugin/servosrc.rs524
5 files changed, 300 insertions, 253 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 34f3cf8fe39..e76dd93a120 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4788,6 +4788,7 @@ dependencies = [
"gstreamer",
"gstreamer-base",
"gstreamer-gl",
+ "gstreamer-gl-sys",
"gstreamer-video",
"lazy_static",
"libservo",
diff --git a/etc/taskcluster/decision_task.py b/etc/taskcluster/decision_task.py
index 194a689a82b..7853a84c2f6 100644
--- a/etc/taskcluster/decision_task.py
+++ b/etc/taskcluster/decision_task.py
@@ -490,7 +490,9 @@ def windows_unit(cached=True):
"mach smoketest --angle",
"mach package --dev",
"mach build --dev --libsimpleservo",
- "mach build --dev -p servo-gst-plugin",
+ # 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",
)
.with_artifacts("repo/target/debug/msi/Servo.exe",
diff --git a/ports/gstplugin/Cargo.toml b/ports/gstplugin/Cargo.toml
index cb72e6b96f8..7926f934360 100644
--- a/ports/gstplugin/Cargo.toml
+++ b/ports/gstplugin/Cargo.toml
@@ -21,7 +21,8 @@ gleam = "0.6"
glib = { version = "0.8", features = ["subclassing"] }
gstreamer = { version = "0.14", features = ["subclassing"] }
gstreamer-base = { version = "0.14", features = ["subclassing"] }
-gstreamer-gl = { version = "0.14", features = ["v1_16"] }
+gstreamer-gl = { version = "0.14" }
+gstreamer-gl-sys = { version = "0.8" }
gstreamer-video = { version = "0.14", features = ["subclassing"] }
log = "0.4"
lazy_static = "1.4"
diff --git a/ports/gstplugin/README.md b/ports/gstplugin/README.md
index 6c0a5d5095b..e61e6d7ea62 100644
--- a/ports/gstplugin/README.md
+++ b/ports/gstplugin/README.md
@@ -24,18 +24,19 @@ To run locally:
```
GST_PLUGIN_PATH=target/gstplugins \
gst-launch-1.0 servosrc \
- ! queue \
- ! video/x-raw,framerate=25/1,width=512,height=512 \
- ! videoflip video-direction=vert \
- ! autovideosink
+ ! videorate \
+ ! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
+ ! glimagesink rotate-method=vertical-flip
```
+*Note*: the following don't work, for some reason the pipeline isn't providing GLMemory.
+
To stream over the network:
```
-GST_PLUGIN_PATH=target/gstplugins \
gst-launch-1.0 servosrc \
- ! queue \
- ! video/x-raw,framerate=25/1,width=512,height=512 \
+ ! videorate \
+ ! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
+ ! gldownload \
! videoconvert \
! videoflip video-direction=vert \
! theoraenc \
@@ -46,9 +47,9 @@ GST_PLUGIN_PATH=target/gstplugins \
To save to a file:
```
GST_PLUGIN_PATH=target/gstplugins \
- gst-launch-1.0 servosrc num-buffers=2000 \
- ! queue \
- ! video/x-raw,framerate=25/1,width=512,height=512 \
+ ! videorate \
+ ! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
+ ! gldownload \
! videoconvert \
! videoflip video-direction=vert \
! theoraenc \
diff --git a/ports/gstplugin/servosrc.rs b/ports/gstplugin/servosrc.rs
index 4aec2122d17..9b0247b6d8d 100644
--- a/ports/gstplugin/servosrc.rs
+++ b/ports/gstplugin/servosrc.rs
@@ -23,6 +23,7 @@ use glib::subclass::object::ObjectImplExt;
use glib::subclass::object::Property;
use glib::subclass::simple::ClassStruct;
use glib::subclass::types::ObjectSubclass;
+use glib::translate::FromGlibPtrBorrow;
use glib::value::Value;
use glib::ParamSpec;
use gstreamer::gst_element_error;
@@ -38,9 +39,6 @@ use gstreamer::ErrorMessage;
use gstreamer::FlowError;
use gstreamer::FlowSuccess;
use gstreamer::Format;
-use gstreamer::Fraction;
-use gstreamer::FractionRange;
-use gstreamer::IntRange;
use gstreamer::LoggableError;
use gstreamer::PadDirection;
use gstreamer::PadPresence;
@@ -49,8 +47,12 @@ use gstreamer::ResourceError;
use gstreamer_base::subclass::base_src::BaseSrcImpl;
use gstreamer_base::BaseSrc;
use gstreamer_base::BaseSrcExt;
-use gstreamer_video::VideoFormat;
-use gstreamer_video::VideoFrameRef;
+use gstreamer_gl::GLContext;
+use gstreamer_gl::GLContextExt;
+use gstreamer_gl::GLContextExtManual;
+use gstreamer_gl_sys::gst_gl_texture_target_to_gl;
+use gstreamer_gl_sys::gst_is_gl_memory;
+use gstreamer_gl_sys::GstGLMemory;
use gstreamer_video::VideoInfo;
use log::debug;
@@ -80,6 +82,7 @@ use surfman_chains::SwapChain;
use surfman_chains_api::SwapChainAPI;
use std::cell::RefCell;
+use std::collections::HashMap;
use std::convert::TryFrom;
use std::rc::Rc;
use std::sync::Mutex;
@@ -96,89 +99,19 @@ struct ServoSrcGfx {
device: Device,
context: Context,
gl: Rc<Gl>,
- fbo: GLuint,
-}
-
-impl ServoSrcGfx {
- fn new() -> ServoSrcGfx {
- let version = surfman::GLVersion { major: 4, minor: 3 };
- let flags = surfman::ContextAttributeFlags::empty();
- let attributes = surfman::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 descriptor = device
- .create_context_descriptor(&attributes)
- .expect("Failed to create descriptor");
- let context = device
- .create_context(&descriptor)
- .expect("Failed to create context");
- let gl = Gl::gl_fns(gl::ffi_gl::Gl::load_with(|s| {
- device.get_proc_address(&context, s)
- }));
-
- // 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);
-
- device.make_context_current(&context).unwrap();
-
- let size = Size2D::new(512, 512);
- let surface_type = SurfaceType::Generic { size };
- let surface = device
- .create_surface(&mut context, SurfaceAccess::GPUCPU, &surface_type)
- .expect("Failed to create surface");
-
- gl.viewport(0, 0, size.width, size.height);
- debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
-
- device
- .bind_surface_to_context(&mut context, surface)
- .expect("Failed to bind surface");
- let fbo = device
- .context_surface_info(&context)
- .expect("Failed to get context info")
- .expect("Failed to get context info")
- .framebuffer_object;
- gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
- debug_assert_eq!(
- (gl.check_framebuffer_status(gl::FRAMEBUFFER), gl.get_error()),
- (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
- );
-
- let fbo = gl.gen_framebuffers(1)[0];
- debug_assert_eq!(
- (gl.check_framebuffer_status(gl::FRAMEBUFFER), gl.get_error()),
- (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
- );
-
- device.make_no_context_current().unwrap();
-
- Self {
- device,
- context,
- gl,
- fbo,
- }
- }
+ read_fbo: GLuint,
+ draw_fbo: GLuint,
}
impl Drop for ServoSrcGfx {
fn drop(&mut self) {
+ self.gl.delete_framebuffers(&[self.read_fbo, self.draw_fbo]);
let _ = self.device.destroy_context(&mut self.context);
}
}
thread_local! {
- static GFX: RefCell<ServoSrcGfx> = RefCell::new(ServoSrcGfx::new());
+ static GFX_CACHE: RefCell<HashMap<GLContext, ServoSrcGfx>> = RefCell::new(HashMap::new());
}
#[derive(Debug)]
@@ -196,6 +129,7 @@ const DEFAULT_URL: &'static str =
struct ServoThread {
receiver: Receiver<ServoSrcMsg>,
swap_chain: SwapChain,
+ gfx: Rc<RefCell<ServoSrcGfx>>,
servo: Servo<ServoSrcWindow>,
}
@@ -204,10 +138,12 @@ impl ServoThread {
let embedder = Box::new(ServoSrcEmbedder);
let window = Rc::new(ServoSrcWindow::new());
let swap_chain = window.swap_chain.clone();
+ let gfx = window.gfx.clone();
let servo = Servo::new(embedder, window);
Self {
receiver,
swap_chain,
+ gfx,
servo,
}
}
@@ -235,8 +171,8 @@ impl ServoThread {
}
fn resize(&mut self, size: Size2D<i32, DevicePixel>) {
- GFX.with(|gfx| {
- let mut gfx = gfx.borrow_mut();
+ {
+ let mut gfx = self.gfx.borrow_mut();
let gfx = &mut *gfx;
self.swap_chain
.resize(&mut gfx.device, &mut gfx.context, size.to_untyped())
@@ -248,6 +184,9 @@ impl ServoThread {
.expect("Failed to get context info")
.expect("Failed to get context info")
.framebuffer_object;
+ gfx.device
+ .make_context_current(&gfx.context)
+ .expect("Failed to make current");
gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
debug_assert_eq!(
(
@@ -256,20 +195,18 @@ impl ServoThread {
),
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
);
- });
+ }
self.servo.handle_events(vec![WindowEvent::Resize]);
}
}
impl Drop for ServoThread {
fn drop(&mut self) {
- GFX.with(|gfx| {
- let mut gfx = gfx.borrow_mut();
- let gfx = &mut *gfx;
- self.swap_chain
- .destroy(&mut gfx.device, &mut gfx.context)
- .expect("Failed to destroy swap chain")
- })
+ let mut gfx = self.gfx.borrow_mut();
+ let gfx = &mut *gfx;
+ self.swap_chain
+ .destroy(&mut gfx.device, &mut gfx.context)
+ .expect("Failed to destroy swap chain")
}
}
@@ -291,102 +228,142 @@ impl EventLoopWaker for ServoSrcEmbedder {
struct ServoSrcWindow {
swap_chain: SwapChain,
+ gfx: Rc<RefCell<ServoSrcGfx>>,
gl: Rc<dyn gleam::gl::Gl>,
}
impl ServoSrcWindow {
fn new() -> Self {
- GFX.with(|gfx| {
- let mut gfx = gfx.borrow_mut();
- let gfx = &mut *gfx;
- let access = SurfaceAccess::GPUCPU;
- gfx.device
- .make_context_current(&mut gfx.context)
- .expect("Failed to make context current");
- debug_assert_eq!(
- (
- gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
- gfx.gl.get_error()
- ),
- (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
- );
- let swap_chain = SwapChain::create_attached(&mut gfx.device, &mut gfx.context, access)
- .expect("Failed to create swap chain");
- let fbo = gfx
- .device
- .context_surface_info(&gfx.context)
- .expect("Failed to get context info")
- .expect("Failed to get context info")
- .framebuffer_object;
- gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
- debug_assert_eq!(
- (
- gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
- gfx.gl.get_error()
- ),
- (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
- );
- let gl = unsafe {
- gleam::gl::GlFns::load_with(|s| gfx.device.get_proc_address(&gfx.context, s))
- };
- Self { swap_chain, gl }
- })
+ let version = surfman::GLVersion { major: 4, minor: 3 };
+ let flags = surfman::ContextAttributeFlags::empty();
+ let attributes = surfman::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 descriptor = device
+ .create_context_descriptor(&attributes)
+ .expect("Failed to create descriptor");
+ let 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)
+ }));
+
+ 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 size = Size2D::new(512, 512);
+ let surface_type = SurfaceType::Generic { size };
+ let surface = device
+ .create_surface(&mut context, access, &surface_type)
+ .expect("Failed to create surface");
+
+ device
+ .bind_surface_to_context(&mut context, surface)
+ .expect("Failed to bind surface");
+ let fbo = device
+ .context_surface_info(&context)
+ .expect("Failed to get context info")
+ .expect("Failed to get context info")
+ .framebuffer_object;
+ gl.viewport(0, 0, size.width, size.height);
+ gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
+ debug_assert_eq!(
+ (gl.check_framebuffer_status(gl::FRAMEBUFFER), gl.get_error()),
+ (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
+ );
+
+ 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(ServoSrcGfx {
+ device,
+ context,
+ gl,
+ read_fbo,
+ draw_fbo,
+ }));
+
+ Self {
+ swap_chain,
+ gfx,
+ gl: gleam,
+ }
}
}
impl WindowMethods for ServoSrcWindow {
fn present(&self) {
- GFX.with(|gfx| {
- debug!("EMBEDDER present");
- let mut gfx = gfx.borrow_mut();
- let gfx = &mut *gfx;
- gfx.device
- .make_context_current(&mut gfx.context)
- .expect("Failed to make context current");
- debug_assert_eq!(
- (
- gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
- gfx.gl.get_error()
- ),
- (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
- );
- let _ = self
- .swap_chain
- .swap_buffers(&mut gfx.device, &mut gfx.context);
- let fbo = gfx
- .device
- .context_surface_info(&gfx.context)
- .expect("Failed to get context info")
- .expect("Failed to get context info")
- .framebuffer_object;
- gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
- debug_assert_eq!(
- (
- gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
- gfx.gl.get_error()
- ),
- (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
- );
- let _ = gfx.device.make_no_context_current();
- })
+ debug!("EMBEDDER present");
+ let mut gfx = self.gfx.borrow_mut();
+ let gfx = &mut *gfx;
+ gfx.device
+ .make_context_current(&mut gfx.context)
+ .expect("Failed to make context current");
+ debug_assert_eq!(
+ (
+ gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
+ gfx.gl.get_error()
+ ),
+ (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
+ );
+ let _ = self
+ .swap_chain
+ .swap_buffers(&mut gfx.device, &mut gfx.context);
+ let fbo = gfx
+ .device
+ .context_surface_info(&gfx.context)
+ .expect("Failed to get context info")
+ .expect("Failed to get context info")
+ .framebuffer_object;
+ gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
+ debug_assert_eq!(
+ (
+ gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
+ gfx.gl.get_error()
+ ),
+ (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
+ );
+ let _ = gfx.device.make_no_context_current();
}
fn make_gl_context_current(&self) {
- GFX.with(|gfx| {
- debug!("EMBEDDER make_context_current");
- let mut gfx = gfx.borrow_mut();
- let gfx = &mut *gfx;
- gfx.device
- .make_context_current(&mut gfx.context)
- .expect("Failed to make context current");
- debug_assert_eq!(
- (
- gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
- gfx.gl.get_error()
- ),
- (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
- );
- })
+ debug!("EMBEDDER make_context_current");
+ let mut gfx = self.gfx.borrow_mut();
+ let gfx = &mut *gfx;
+ gfx.device
+ .make_context_current(&mut gfx.context)
+ .expect("Failed to make context current");
+ debug_assert_eq!(
+ (
+ gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
+ gfx.gl.get_error()
+ ),
+ (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
+ );
}
fn gl(&self) -> Rc<dyn gleam::gl::Gl> {
@@ -432,6 +409,12 @@ static PROPERTIES: [Property; 1] = [Property("url", |name| {
)
})];
+const CAPS: &str = "video/x-raw(memory:GLMemory),
+ format={RGBA,RGBx},
+ width=[1,2147483647],
+ height=[1,2147483647],
+ framerate=[0/1,2147483647/1]";
+
impl ObjectSubclass for ServoSrc {
const NAME: &'static str = "ServoSrc";
// gstreamer-gl doesn't have support for GLBaseSrc yet
@@ -464,21 +447,7 @@ impl ObjectSubclass for ServoSrc {
env!("CARGO_PKG_AUTHORS"),
);
- let src_caps = Caps::new_simple(
- "video/x-raw",
- &[
- ("format", &VideoFormat::Bgrx.to_string()),
- ("width", &IntRange::<i32>::new(1, std::i32::MAX)),
- ("height", &IntRange::<i32>::new(1, std::i32::MAX)),
- (
- "framerate",
- &FractionRange::new(
- Fraction::new(1, std::i32::MAX),
- Fraction::new(std::i32::MAX, 1),
- ),
- ),
- ],
- );
+ let src_caps = Caps::from_string(CAPS).unwrap();
let src_pad_template =
PadTemplate::new("src", PadDirection::Src, PadPresence::Always, &src_caps).unwrap();
klass.add_pad_template(src_pad_template);
@@ -525,6 +494,9 @@ impl ObjectImpl for ServoSrc {
impl ElementImpl for ServoSrc {}
+thread_local! {
+ static GL: RefCell<Option<Rc<Gl>>> = RefCell::new(None);
+}
impl BaseSrcImpl for ServoSrc {
fn set_caps(&self, _src: &BaseSrc, outcaps: &Caps) -> Result<(), LoggableError> {
let info = VideoInfo::from_caps(outcaps)
@@ -563,35 +535,85 @@ impl BaseSrcImpl for ServoSrc {
_length: u32,
buffer: &mut BufferRef,
) -> Result<FlowSuccess, FlowError> {
- let guard = self.info.lock().map_err(|_| {
- gst_element_error!(src, CoreError::Negotiation, ["Lock poisoned"]);
- FlowError::NotNegotiated
- })?;
- let info = guard.as_ref().ok_or_else(|| {
- gst_element_error!(src, CoreError::Negotiation, ["Caps not set yet"]);
- FlowError::NotNegotiated
+ let memory = buffer.get_all_memory().ok_or_else(|| {
+ gst_element_error!(src, CoreError::Failed, ["Failed to get memory"]);
+ FlowError::Error
})?;
- let mut frame = VideoFrameRef::from_buffer_ref_writable(buffer, info).ok_or_else(|| {
- gst_element_error!(
- src,
- CoreError::Failed,
- ["Failed to map output buffer writable"]
- );
+ let memory = unsafe { memory.into_ptr() };
+ if unsafe { gst_is_gl_memory(memory) } == 0 {
+ gst_element_error!(src, CoreError::Failed, ["Memory isn't GL memory"]);
+ return Err(FlowError::Error);
+ }
+ let gl_memory = unsafe { (memory as *mut GstGLMemory).as_ref() }.ok_or_else(|| {
+ gst_element_error!(src, CoreError::Failed, ["Memory is null"]);
FlowError::Error
})?;
- let height = frame.height() as i32;
- let width = frame.width() as i32;
+
+ let gl_context = unsafe { GLContext::from_glib_borrow(gl_memory.mem.context) };
+ let draw_texture_id = gl_memory.tex_id;
+ let draw_texture_target = unsafe { gst_gl_texture_target_to_gl(gl_memory.tex_target) };
+ let height = gl_memory.info.height;
+ let width = gl_memory.info.width;
let size = Size2D::new(width, height);
- let format = frame.format();
- debug!(
- "Filling servosrc buffer {}x{} {:?} {:?}",
- width, height, format, frame,
- );
- let data = frame.plane_data_mut(0).unwrap();
+ debug!("Filling texture {} {}x{}", draw_texture_id, width, height);
+
+ gl_context.activate(true).map_err(|_| {
+ gst_element_error!(src, CoreError::Failed, ["Failed to activate GL context"]);
+ FlowError::Error
+ })?;
+
+ GFX_CACHE.with(|gfx_cache| {
+ 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 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];
+ ServoSrcGfx {
+ device,
+ context,
+ gl,
+ read_fbo,
+ draw_fbo,
+ }
+ });
+
+ debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR);
+
+ // Save the current GL state
+ let mut bound_fbos = [0, 0];
+ unsafe {
+ gfx.gl
+ .get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING, &mut bound_fbos[0..]);
+ gfx.gl
+ .get_integer_v(gl::READ_FRAMEBUFFER_BINDING, &mut bound_fbos[1..]);
+ }
+
+ gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, gfx.draw_fbo);
+ gfx.gl.framebuffer_texture_2d(
+ gl::FRAMEBUFFER,
+ gl::COLOR_ATTACHMENT0,
+ gl::TEXTURE_2D,
+ 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(gl::COLOR_BUFFER_BIT);
+ debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR);
- GFX.with(|gfx| {
- let mut gfx = gfx.borrow_mut();
- let gfx = &mut *gfx;
if let Some(surface) = self.swap_chain.take_surface() {
let surface_size = Size2D::from_untyped(gfx.device.surface_info(&surface).size);
if size != surface_size {
@@ -600,36 +622,27 @@ impl BaseSrcImpl for ServoSrc {
let _ = self.sender.send(ServoSrcMsg::Resize(size));
}
- gfx.device.make_context_current(&gfx.context).unwrap();
- debug_assert_eq!(
- (
- gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
- gfx.gl.get_error()
- ),
- (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
- );
-
- gfx.gl.viewport(0, 0, width, height);
- debug_assert_eq!(
- (
- gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
- gfx.gl.get_error()
- ),
- (gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
- );
-
let surface_texture = gfx
.device
.create_surface_texture(&mut gfx.context, surface)
.unwrap();
- let texture_id = surface_texture.gl_texture();
+ let read_texture_id = surface_texture.gl_texture();
+ let read_texture_target = gfx.device.surface_gl_texture_target();
- gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, gfx.fbo);
+ gfx.gl.bind_framebuffer(gl::READ_FRAMEBUFFER, gfx.read_fbo);
+ gfx.gl.framebuffer_texture_2d(
+ gl::READ_FRAMEBUFFER,
+ gl::COLOR_ATTACHMENT0,
+ read_texture_target,
+ read_texture_id,
+ 0,
+ );
+ gfx.gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, gfx.draw_fbo);
gfx.gl.framebuffer_texture_2d(
- gl::FRAMEBUFFER,
+ gl::DRAW_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
- gfx.device.surface_gl_texture_target(),
- texture_id,
+ draw_texture_target,
+ draw_texture_id,
0,
);
debug_assert_eq!(
@@ -640,15 +653,31 @@ impl BaseSrcImpl for ServoSrc {
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
);
- // TODO: use GL memory to avoid readback
- gfx.gl.read_pixels_into_buffer(
+ 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)
+ );
+
+ debug!(
+ "Filling with {}/{} {}",
+ read_texture_id, read_texture_target, surface_size
+ );
+ gfx.gl.blit_framebuffer(
+ 0,
+ 0,
+ surface_size.width,
+ surface_size.height,
0,
0,
width,
height,
- gl::BGRA,
- gl::UNSIGNED_BYTE,
- data,
+ gl::COLOR_BUFFER_BIT,
+ gl::NEAREST,
);
debug_assert_eq!(
(
@@ -658,15 +687,28 @@ impl BaseSrcImpl for ServoSrc {
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
);
- gfx.device.make_no_context_current().unwrap();
-
let surface = gfx
.device
.destroy_surface_texture(&mut gfx.context, surface_texture)
.unwrap();
self.swap_chain.recycle_surface(surface);
+ } else {
+ debug!("Failed to get current surface");
}
+
+ // Restore the GL state
+ gfx.gl
+ .bind_framebuffer(gl::DRAW_FRAMEBUFFER, bound_fbos[0] as GLuint);
+ gfx.gl
+ .bind_framebuffer(gl::READ_FRAMEBUFFER, bound_fbos[1] as GLuint);
+ 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
+ })?;
+
let _ = self.sender.send(ServoSrcMsg::Heartbeat);
Ok(FlowSuccess::Ok)
}