GStreamer plugin should use GLMemory

This commit is contained in:
Alan Jeffrey 2019-11-26 17:40:07 -06:00
parent bb00a83ec4
commit 06a10c5b65
4 changed files with 299 additions and 254 deletions

1
Cargo.lock generated
View file

@ -4788,6 +4788,7 @@ dependencies = [
"gstreamer",
"gstreamer-base",
"gstreamer-gl",
"gstreamer-gl-sys",
"gstreamer-video",
"lazy_static",
"libservo",

View file

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

View file

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

View file

@ -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 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 = buffer.get_all_memory().ok_or_else(|| {
gst_element_error!(src, CoreError::Failed, ["Failed to get memory"]);
FlowError::Error
})?;
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 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();
GFX.with(|gfx| {
let mut gfx = gfx.borrow_mut();
let gfx = &mut *gfx;
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);
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);
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::FRAMEBUFFER,
gl::READ_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gfx.device.surface_gl_texture_target(),
texture_id,
read_texture_target,
read_texture_id,
0,
);
gfx.gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, gfx.draw_fbo);
gfx.gl.framebuffer_texture_2d(
gl::DRAW_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
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)
}