diff --git a/video/gtk4/src/sink/frame.rs b/video/gtk4/src/sink/frame.rs index 0ebdb690..3e4bcbd0 100644 --- a/video/gtk4/src/sink/frame.rs +++ b/video/gtk4/src/sink/frame.rs @@ -21,7 +21,7 @@ pub(crate) struct Frame { frame: gst_video::VideoFrame, overlays: Vec, #[cfg(any(target_os = "macos", feature = "gst_gl"))] - gst_context: Option, + wrapped_context: Option, } #[derive(Debug)] @@ -100,7 +100,7 @@ fn video_frame_to_gl_texture( cached_textures: &mut HashMap, used_textures: &mut HashSet, gdk_context: &gdk::GLContext, - gst_context: &gst_gl::GLContext, + wrapped_context: &gst_gl::GLContext, ) -> (gdk::Texture, f64) { let texture_id = frame.texture_id(0).expect("Invalid texture id") as usize; @@ -116,7 +116,7 @@ fn video_frame_to_gl_texture( let height = frame.height(); let sync_meta = frame.buffer().meta::().unwrap(); - sync_meta.wait(gst_context); + sync_meta.wait(wrapped_context); let texture = unsafe { gdk::GLTexture::with_release_func( @@ -156,19 +156,21 @@ impl Frame { } #[cfg(any(target_os = "macos", feature = "gst_gl"))] { - if let (Some(gdk_ctx), Some(gst_ctx)) = (gdk_context, self.gst_context.as_ref()) { + if let (Some(gdk_ctx), Some(wrapped_ctx)) = + (gdk_context, self.wrapped_context.as_ref()) + { video_frame_to_gl_texture( self.frame, cached_textures, &mut used_textures, gdk_ctx, - gst_ctx, + wrapped_ctx, ) } else { // This will fail badly if the video frame was actually mapped as GL texture // but this case can't really happen as we only do that if we actually have a // GDK GL context. - assert!(self.gst_context.is_none()); + assert!(self.wrapped_context.is_none()); video_frame_to_memory_texture(self.frame, cached_textures, &mut used_textures) } } @@ -208,7 +210,12 @@ impl Frame { pub(crate) fn new( buffer: &gst::Buffer, info: &gst_video::VideoInfo, - #[allow(unused_variables)] have_gl_context: bool, + #[cfg(any(target_os = "macos", feature = "gst_gl"))] wrapped_context: Option< + &gst_gl::GLContext, + >, + #[allow(unused_variables)] + #[cfg(not(any(target_os = "macos", feature = "gst_gl")))] + wrapped_context: Option<&()>, ) -> Result { // Empty buffers get filtered out in show_frame debug_assert!(buffer.n_memory() > 0); @@ -225,35 +232,35 @@ impl Frame { } #[cfg(any(target_os = "macos", feature = "gst_gl"))] { - let is_buffer_gl = buffer + // Check we received a buffer with GL memory and if the context of that memory + // can share with the wrapped context around the GDK GL context. + // + // If not it has to be uploaded to the GPU. + let memory_ctx = buffer .peek_memory(0) .downcast_memory_ref::() - .is_some(); - - if !is_buffer_gl || !have_gl_context { - frame = Self { - frame: gst_video::VideoFrame::from_buffer_readable(buffer.clone(), info) - .map_err(|_| gst::FlowError::Error)?, - overlays: vec![], - gst_context: None, - }; - } else { - let gst_ctx = buffer - .peek_memory(0) - .downcast_memory_ref::() - .map(|m| m.context()) - .expect("Failed to retrieve the GstGL Context."); + .and_then(|m| { + let ctx = m.context(); + if wrapped_context + .map_or(false, |wrapped_context| wrapped_context.can_share(ctx)) + { + Some(ctx) + } else { + None + } + }); + if let Some(memory_ctx) = memory_ctx { let mapped_frame = if let Some(meta) = buffer.meta::() { - meta.set_sync_point(gst_ctx); + meta.set_sync_point(memory_ctx); gst_video::VideoFrame::from_buffer_readable_gl(buffer.clone(), info) .map_err(|_| gst::FlowError::Error)? } else { let mut buffer = buffer.clone(); { let buffer = buffer.make_mut(); - let meta = gst_gl::GLSyncMeta::add(buffer, gst_ctx); - meta.set_sync_point(gst_ctx); + let meta = gst_gl::GLSyncMeta::add(buffer, memory_ctx); + meta.set_sync_point(memory_ctx); } gst_video::VideoFrame::from_buffer_readable_gl(buffer, info) .map_err(|_| gst::FlowError::Error)? @@ -262,7 +269,14 @@ impl Frame { frame = Self { frame: mapped_frame, overlays: vec![], - gst_context: Some(gst_ctx.clone()), + wrapped_context: Some(wrapped_context.unwrap().clone()), + }; + } else { + frame = Self { + frame: gst_video::VideoFrame::from_buffer_readable(buffer.clone(), info) + .map_err(|_| gst::FlowError::Error)?, + overlays: vec![], + wrapped_context: None, }; } } diff --git a/video/gtk4/src/sink/imp.rs b/video/gtk4/src/sink/imp.rs index 4c59fbb8..3386a02c 100644 --- a/video/gtk4/src/sink/imp.rs +++ b/video/gtk4/src/sink/imp.rs @@ -30,8 +30,23 @@ use crate::utils; use gst_gl::prelude::GLContextExt as GstGLContextExt; #[cfg(any(target_os = "macos", feature = "gst_gl"))] use gst_gl::prelude::*; + +// Global GL context that is created by the first sink and kept around until the end of the +// process. This is provided to other elements in the pipeline to make sure they create GL contexts +// that are sharing with the GTK GL context. #[cfg(any(target_os = "macos", feature = "gst_gl"))] -use std::sync::atomic::{AtomicBool, Ordering}; +enum GLContext { + Uninitialized, + Unsupported, + Initialized { + display: gst_gl::GLDisplay, + wrapped_context: gst_gl::GLContext, + gdk_context: ThreadGuard, + }, +} + +#[cfg(any(target_os = "macos", feature = "gst_gl"))] +static GL_CONTEXT: Mutex = Mutex::new(GLContext::Uninitialized); static CAT: Lazy = Lazy::new(|| { gst::DebugCategory::new( @@ -47,15 +62,7 @@ pub struct PaintableSink { info: Mutex>, sender: Mutex>>, pending_frame: Mutex>, - #[cfg(any(target_os = "macos", feature = "gst_gl"))] - gst_display: Mutex>, - #[cfg(any(target_os = "macos", feature = "gst_gl"))] - gst_app_context: Mutex>, - #[cfg(any(target_os = "macos", feature = "gst_gl"))] - gst_context: Mutex>, cached_caps: Mutex>, - #[cfg(any(target_os = "macos", feature = "gst_gl"))] - have_gl_context: AtomicBool, } impl Drop for PaintableSink { @@ -147,7 +154,9 @@ impl ElementImpl for PaintableSink { for features in [ None, + #[cfg(any(target_os = "macos", feature = "gst_gl"))] Some(&["memory:GLMemory", "meta:GstVideoOverlayComposition"][..]), + #[cfg(any(target_os = "macos", feature = "gst_gl"))] Some(&["memory:GLMemory"][..]), Some(&["memory:SystemMemory", "meta:GstVideoOverlayComposition"][..]), Some(&["meta:GstVideoOverlayComposition"][..]), @@ -210,16 +219,33 @@ impl ElementImpl for PaintableSink { drop(paintable); + // Notify the pipeline about the GL display and wrapped context so that any other + // elements in the pipeline ideally use the same / create GL contexts that are + // sharing with this one. #[cfg(any(target_os = "macos", feature = "gst_gl"))] { - if self.have_gl_context.load(Ordering::Relaxed) { - if self.initialize_gl_wrapper() { - // We must have a display at this point. - let display = self.gst_display.lock().unwrap().clone().unwrap(); - gst_gl::gl_element_propagate_display_context(&*self.obj(), &display); - } else { - self.have_gl_context.store(false, Ordering::Relaxed); + let gl_context = GL_CONTEXT.lock().unwrap(); + if let GLContext::Initialized { + display, + wrapped_context, + .. + } = &*gl_context + { + let display = display.clone(); + let wrapped_context = wrapped_context.clone(); + drop(gl_context); + + gst_gl::gl_element_propagate_display_context(&*self.obj(), &display); + let mut ctx = gst::Context::new("gst.gl.app_context", true); + { + let ctx = ctx.get_mut().unwrap(); + ctx.structure_mut().set("context", &wrapped_context); } + let _ = self.obj().post_message( + gst::message::HaveContext::builder(ctx) + .src(&*self.obj()) + .build(), + ); } } } @@ -243,12 +269,6 @@ impl ElementImpl for PaintableSink { } }); } - #[cfg(any(target_os = "macos", feature = "gst_gl"))] - gst::StateChange::ReadyToNull => { - let _ = self.gst_context.lock().unwrap().take(); - let _ = self.gst_app_context.lock().unwrap().take(); - let _ = self.gst_display.lock().unwrap().take(); - } _ => (), } @@ -269,25 +289,24 @@ impl BaseSinkImpl for PaintableSink { templ[0].caps().clone() }); - gst::debug!(CAT, imp: self, "Advertising our own caps: {:?}", &tmp_caps); + gst::debug!(CAT, imp: self, "Advertising our own caps: {tmp_caps:?}"); if let Some(filter_caps) = filter { gst::debug!( CAT, imp: self, - "Intersecting with filter caps: {:?}", - &filter_caps + "Intersecting with filter caps: {filter_caps:?}", ); tmp_caps = filter_caps.intersect_with_mode(&tmp_caps, gst::CapsIntersectMode::First); }; - gst::debug!(CAT, imp: self, "Returning caps: {:?}", &tmp_caps); + gst::debug!(CAT, imp: self, "Returning caps: {tmp_caps:?}"); Some(tmp_caps) } fn set_caps(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> { - gst::debug!(CAT, imp: self, "Setting caps {:?}", caps); + gst::debug!(CAT, imp: self, "Setting caps {caps:?}"); let video_info = gst_video::VideoInfo::from_caps(caps) .map_err(|_| gst::loggable_error!(CAT, "Invalid caps"))?; @@ -309,78 +328,21 @@ impl BaseSinkImpl for PaintableSink { // TODO: Provide a preferred "window size" here for higher-resolution rendering query.add_allocation_meta::(None); - #[cfg(not(any(target_os = "macos", feature = "gst_gl")))] - { - Ok(()) - } - #[cfg(any(target_os = "macos", feature = "gst_gl"))] { - // Early return if there is no context initialized - let gst_context = match &*self.gst_context.lock().unwrap() { - None => { - gst::debug!( - CAT, - imp: self, - "Found no GL Context during propose_allocation." - ); - return Ok(()); - } - Some(gst_context) => gst_context.clone(), - }; - - // GL specific things - let (caps, need_pool) = query.get_owned(); - if caps.is_empty() || caps.is_any() { - return Ok(()); - } - - if let Some(f) = caps.features(0) { - if !f.contains("memory:GLMemory") { - gst::debug!( - CAT, - imp: self, - "No 'memory:GLMemory' feature in caps: {}", - caps - ) - } - } - - let info = gst_video::VideoInfo::from_caps(&caps) - .map_err(|_| gst::loggable_error!(CAT, "Failed to get VideoInfo from caps"))?; - - let size = info.size() as u32; - let buffer_pool = if need_pool { - let buffer_pool = gst_gl::GLBufferPool::new(&gst_context); - gst::debug!(CAT, imp: self, "Creating new Pool"); - - let mut config = buffer_pool.config(); - config.set_params(Some(&caps), size, 0, 0); - config.add_option("GstBufferPoolOptionGLSyncMeta"); - - if let Err(err) = buffer_pool.set_config(config) { - return Err(gst::loggable_error!( - CAT, - format!("Failed to set config in the GL BufferPool.: {}", err) - )); - } - - Some(buffer_pool) - } else { - None - }; - - // we need at least 2 buffer because we hold on to the last one - query.add_allocation_pool(buffer_pool.as_ref(), size, 2, 0); - - if gst_context.check_feature("GL_ARB_sync") - || gst_context.check_feature("GL_EXT_EGL_sync") + if let GLContext::Initialized { + wrapped_context, .. + } = &*GL_CONTEXT.lock().unwrap() { - query.add_allocation_meta::(None) + if wrapped_context.check_feature("GL_ARB_sync") + || wrapped_context.check_feature("GL_EXT_EGL_sync") + { + query.add_allocation_meta::(None) + } } - - Ok(()) } + + Ok(()) } fn query(&self, query: &mut gst::QueryRef) -> bool { @@ -391,21 +353,27 @@ impl BaseSinkImpl for PaintableSink { gst::QueryViewMut::Context(q) => { // Avoid holding the locks while we respond to the query // The objects are ref-counted anyway. - let (gst_display, app_ctx, gst_ctx) = ( - self.gst_display.lock().unwrap().clone(), - self.gst_app_context.lock().unwrap().clone(), - self.gst_context.lock().unwrap().clone(), - ); + let mut display_clone = None; + let mut wrapped_context_clone = None; + if let GLContext::Initialized { + display, + wrapped_context, + .. + } = &*GL_CONTEXT.lock().unwrap() + { + display_clone = Some(display.clone()); + wrapped_context_clone = Some(wrapped_context.clone()); + } - if let (Some(gst_display), Some(app_ctx), Some(gst_ctx)) = - (gst_display, app_ctx, gst_ctx) + if let (Some(display), Some(wrapped_context)) = + (display_clone, wrapped_context_clone) { return gst_gl::functions::gl_handle_context_query( &*self.obj(), q, - Some(&gst_display), - Some(&gst_ctx), - Some(&app_ctx), + Some(&display), + None::<&gst_gl::GLContext>, + Some(&wrapped_context), ); } @@ -436,17 +404,25 @@ impl VideoSinkImpl for PaintableSink { gst::FlowError::NotNegotiated })?; - let have_gl_context = { + let wrapped_context = { #[cfg(not(any(target_os = "macos", feature = "gst_gl")))] { - false + None } #[cfg(any(target_os = "macos", feature = "gst_gl"))] { - self.have_gl_context.load(Ordering::Relaxed) + let gl_context = GL_CONTEXT.lock().unwrap(); + if let GLContext::Initialized { + wrapped_context, .. + } = &*gl_context + { + Some(wrapped_context.clone()) + } else { + None + } } }; - let frame = Frame::new(buffer, info, have_gl_context).map_err(|err| { + let frame = Frame::new(buffer, info, wrapped_context.as_ref()).map_err(|err| { gst::error!(CAT, imp: self, "Failed to map video frame"); err })?; @@ -498,7 +474,7 @@ impl PaintableSink { #[cfg(any(target_os = "macos", feature = "gst_gl"))] { // Filter out GL caps from the template pads if we have no context - if !self.have_gl_context.load(Ordering::Relaxed) { + if !matches!(&*GL_CONTEXT.lock().unwrap(), GLContext::Initialized { .. }) { tmp_caps = tmp_caps .iter_with_features() .filter(|(_, features)| !features.contains("memory:GLMemory")) @@ -514,32 +490,37 @@ impl PaintableSink { } fn create_paintable(&self, paintable_storage: &mut MutexGuard>>) { - #[allow(unused_mut)] - let mut ctx = None; - #[cfg(any(target_os = "macos", feature = "gst_gl"))] { - if let Some(c) = self.realize_context() { - self.have_gl_context.store(true, Ordering::Relaxed); - ctx = Some(c); - } + self.initialize_gl_context(); } self.configure_caps(); - self.initialize_paintable(ctx, paintable_storage); + self.initialize_paintable(paintable_storage); } fn initialize_paintable( &self, - gl_context: Option>, paintable_storage: &mut MutexGuard>>, ) { gst::debug!(CAT, imp: self, "Initializing paintable"); let paintable = utils::invoke_on_main_thread(|| { - // grab the context out of the fragile - let ctx = gl_context.map(|f| f.into_inner()); - ThreadGuard::new(Paintable::new(ctx)) + #[cfg(any(target_os = "macos", feature = "gst_gl"))] + { + let gdk_context = if let GLContext::Initialized { gdk_context, .. } = + &*GL_CONTEXT.lock().unwrap() + { + Some(gdk_context.get_ref().clone()) + } else { + None + }; + ThreadGuard::new(Paintable::new(gdk_context)) + } + #[cfg(not(any(target_os = "macos", feature = "gst_gl")))] + { + ThreadGuard::new(Paintable::new(None)) + } }); // The channel for the SinkEvents @@ -559,207 +540,144 @@ impl PaintableSink { } #[cfg(any(target_os = "macos", feature = "gst_gl"))] - fn realize_context(&self) -> Option> { + fn initialize_gl_context(&self) { gst::debug!(CAT, imp: self, "Realizing GDK GL Context"); let self_ = self.to_owned(); - utils::invoke_on_main_thread(move || -> Option> { - gst::debug!( - CAT, - imp: self_, - "Realizing GDK GL Context from main context" - ); - - // This can return NULL but only happens in 2 situations: - // * If the function is called before gtk_init - // * If the function is called after gdk_display_close(default_display) - // Both of which are treated as programming errors. - // - // However, when we are building the docs, gtk_init doesn't get called - // and this would cause the documentation generation to error. - // Thus its okayish to return None here and fallback to software - // rendering, since this path isn't going to be used by applications - // anyway. - // - // FIXME: add a couple more gtk_init checks across the codebase where - // applicable since this is no longer going to panic. - let display = gdk::Display::default()?; - let ctx = match display.create_gl_context() { - Ok(ctx) => ctx, - Err(err) => { - gst::warning!(CAT, imp: self_, "Failed to create GDK GL Context: {err}"); - return None; - } - }; - - match ctx.type_().name() { - #[cfg(all(target_os = "linux", feature = "x11egl"))] - "GdkX11GLContextEGL" => (), - #[cfg(all(target_os = "linux", feature = "x11glx"))] - "GdkX11GLContextGLX" => (), - #[cfg(all(target_os = "linux", feature = "wayland"))] - "GdkWaylandGLContext" => (), - #[cfg(target_os = "macos")] - "GdkMacosGLContext" => (), - display => { - gst::error!(CAT, imp: self_, "Unsupported GDK display {display} for GL"); - return None; - } - } - - gst::info!(CAT, imp: &self_, "Realizing GDK GL Context",); - - match ctx.realize() { - Ok(_) => { - gst::info!(CAT, imp: self_, "Successfully realized GDK GL Context",); - Some(ThreadGuard::new(ctx)) - } - Err(err) => { - gst::warning!(CAT, imp: self_, "Failed to realize GDK GL Context: {err}",); - None - } - } - }) + utils::invoke_on_main_thread(move || { + self_.initialize_gl_context_main(); + }); } #[cfg(any(target_os = "macos", feature = "gst_gl"))] - fn initialize_gl_wrapper(&self) -> bool { - gst::info!(CAT, imp: self, "Initializing GDK GL Context"); - let self_ = self.to_owned(); - utils::invoke_on_main_thread(move || self_.initialize_gl()) - } + fn initialize_gl_context_main(&self) { + gst::debug!(CAT, imp: self, "Realizing GDK GL Context from main thread"); - #[cfg(any(target_os = "macos", feature = "gst_gl"))] - fn initialize_gl(&self) -> bool { - let ctx = { - let paintable = self.paintable.lock().unwrap(); - // Impossible to not have a paintable and GL context at this point - paintable.as_ref().unwrap().get_ref().context().unwrap() - }; + let mut gl_context_guard = GL_CONTEXT.lock().unwrap(); + if !matches!(&*gl_context_guard, GLContext::Uninitialized) { + gst::debug!(CAT, imp: self, "Already initialized GL context before"); + return; + } + *gl_context_guard = GLContext::Unsupported; - let display = gtk::prelude::GLContextExt::display(&ctx) - .expect("Failed to get GDK Display from GDK Context."); - ctx.make_current(); - - let mut app_ctx_guard = self.gst_app_context.lock().unwrap(); - let mut display_guard = self.gst_display.lock().unwrap(); - - match ctx.type_().name() { - #[cfg(all(target_os = "linux", feature = "x11egl"))] - "GdkX11GLContextEGL" => { - self.initialize_x11egl(display, &mut display_guard, &mut app_ctx_guard); - } - #[cfg(all(target_os = "linux", feature = "x11glx"))] - "GdkX11GLContextGLX" => { - self.initialize_x11glx(display, &mut display_guard, &mut app_ctx_guard); - } - #[cfg(all(target_os = "linux", feature = "wayland"))] - "GdkWaylandGLContext" => { - self.initialize_waylandegl(display, &mut display_guard, &mut app_ctx_guard); - } - #[cfg(target_os = "macos")] - "GdkMacosGLContext" => { - self.initialize_macosgl(display, &mut display_guard, &mut app_ctx_guard); - } - _ => { - unreachable!("Unsupported GDK display {display} for GL"); - } - }; - - // This should have been initialized once we are done with the platform checks - let app_ctx = match &*app_ctx_guard { - None => { - assert!(display_guard.is_none()); - return false; - } - Some(app_ctx) => app_ctx, - }; - - let display = match &*display_guard { - None => return false, + // This can return NULL but only happens in 2 situations: + // * If the function is called before gtk_init + // * If the function is called after gdk_display_close(default_display) + // Both of which are treated as programming errors. + // + // However, when we are building the docs, gtk_init doesn't get called + // and this would cause the documentation generation to error. + // Thus its okayish to return None here and fallback to software + // rendering, since this path isn't going to be used by applications + // anyway. + // + // FIXME: add a couple more gtk_init checks across the codebase where + // applicable since this is no longer going to panic. + let gdk_display = match gdk::Display::default() { Some(display) => display, + None => { + gst::warning!(CAT, imp: self, "Failed to retrieve GDK display"); + return; + } + }; + let gdk_context = match gdk_display.create_gl_context() { + Ok(gdk_context) => gdk_context, + Err(err) => { + gst::warning!(CAT, imp: self, "Failed to create GDK GL Context: {err}"); + return; + } }; - match app_ctx.activate(true) { - Ok(_) => gst::info!(CAT, imp: self, "Successfully activated GL Context."), + match gdk_context.type_().name() { + #[cfg(all(target_os = "linux", feature = "x11egl"))] + "GdkX11GLContextEGL" => (), + #[cfg(all(target_os = "linux", feature = "x11glx"))] + "GdkX11GLContextGLX" => (), + #[cfg(all(target_os = "linux", feature = "wayland"))] + "GdkWaylandGLContext" => (), + #[cfg(target_os = "macos")] + "GdkMacosGLContext" => (), + display => { + gst::error!(CAT, imp: self, "Unsupported GDK display {display} for GL"); + return; + } + } + + gst::info!(CAT, imp: self, "Realizing GDK GL Context",); + + if let Err(err) = gdk_context.realize() { + gst::warning!(CAT, imp: self, "Failed to realize GDK GL Context: {err}"); + return; + } + + gst::info!(CAT, imp: self, "Successfully realized GDK GL Context"); + + gdk_context.make_current(); + + let res = match gdk_context.type_().name() { + #[cfg(all(target_os = "linux", feature = "x11egl"))] + "GdkX11GLContextEGL" => self.initialize_x11egl(gdk_display), + #[cfg(all(target_os = "linux", feature = "x11glx"))] + "GdkX11GLContextGLX" => self.initialize_x11glx(gdk_display), + #[cfg(all(target_os = "linux", feature = "wayland"))] + "GdkWaylandGLContext" => self.initialize_waylandegl(gdk_display), + #[cfg(target_os = "macos")] + "GdkMacosGLContext" => self.initialize_macosgl(gdk_display), + display_type => { + unreachable!("Unsupported GDK display {display_type} for GL"); + } + }; + + let (display, wrapped_context) = match res { + Some((display, wrapped_context)) => (display, wrapped_context), + None => { + return; + } + }; + + match wrapped_context.activate(true) { + Ok(_) => gst::info!(CAT, imp: self, "Successfully activated GL Context"), Err(_) => { gst::error!(CAT, imp: self, "Failed to activate GL context",); - *app_ctx_guard = None; - *display_guard = None; - return false; + return; } }; - if let Err(err) = app_ctx.fill_info() { + if let Err(err) = wrapped_context.fill_info() { gst::error!( CAT, imp: self, "Failed to fill info on the GL Context: {err}", ); // Deactivate the context upon failure - if app_ctx.activate(false).is_err() { + if wrapped_context.activate(false).is_err() { gst::error!( CAT, imp: self, "Failed to deactivate the context after failing fill info", ); } - *app_ctx_guard = None; - *display_guard = None; - - return false; + return; } - if app_ctx.activate(false).is_err() { - gst::error!(CAT, imp: self, "Failed to deactivate GL context",); - *app_ctx_guard = None; - *display_guard = None; - return false; - } + gst::info!(CAT, imp: self, "Successfully initialized GL Context"); - gst::info!( - CAT, - imp: self, - "Successfully deactivated GL Context after fill_info" - ); - - let gst_context = match display.create_context(app_ctx) { - Ok(gst_context) => gst_context, - Err(err) => { - gst::error!(CAT, imp: self, "Could not create GL context: {err}"); - *app_ctx_guard = None; - *display_guard = None; - return false; - } + *gl_context_guard = GLContext::Initialized { + display, + wrapped_context, + gdk_context: ThreadGuard::new(gdk_context), }; - - match display.add_context(&gst_context) { - Ok(_) => { - let mut gst_ctx_guard = self.gst_context.lock().unwrap(); - gst::info!(CAT, imp: self, "Successfully initialized GL Context"); - gst_ctx_guard.replace(gst_context); - true - } - Err(_) => { - gst::error!(CAT, imp: self, "Could not add GL context to display"); - *app_ctx_guard = None; - *display_guard = None; - false - } - } } #[cfg(all(target_os = "linux", feature = "x11egl"))] fn initialize_x11egl( &self, display: gdk::Display, - display_guard: &mut Option, - app_ctx_guard: &mut Option, - ) { + ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> { gst::info!( CAT, imp: self, - "Initializing GL for x11 EGL backend and display." + "Initializing GL for x11 EGL backend and display" ); let platform = gst_gl::GLPlatform::EGL; @@ -767,37 +685,37 @@ impl PaintableSink { let gl_ctx = gst_gl::GLContext::current_gl_context(platform); if gl_ctx == 0 { - gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext",); - return; + gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext"); + return None; } // FIXME: bindings unsafe { use glib::translate::*; - let d = display.downcast::().unwrap(); - let x11_display = gdk_x11::ffi::gdk_x11_display_get_egl_display(d.to_glib_none().0); + let display = display.downcast::().unwrap(); + let x11_display = + gdk_x11::ffi::gdk_x11_display_get_egl_display(display.to_glib_none().0); if x11_display.is_null() { gst::error!(CAT, imp: self, "Failed to get EGL display"); - return; + return None; } let gst_display = gst_gl_egl::ffi::gst_gl_display_egl_new_with_egl_display(x11_display); let gst_display = gst_gl::GLDisplay::from_glib_full(gst_display as *mut gst_gl::ffi::GstGLDisplay); - let gst_app_context = + let wrapped_context = gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api); - let gst_app_context = match gst_app_context { + let wrapped_context = match wrapped_context { None => { gst::error!(CAT, imp: self, "Failed to create wrapped GL context"); - return; + return None; } - Some(gst_app_context) => gst_app_context, + Some(wrapped_context) => wrapped_context, }; - display_guard.replace(gst_display); - app_ctx_guard.replace(gst_app_context); + Some((gst_display, wrapped_context)) } } @@ -805,13 +723,11 @@ impl PaintableSink { fn initialize_x11glx( &self, display: gdk::Display, - display_guard: &mut Option, - app_ctx_guard: &mut Option, - ) { + ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> { gst::info!( CAT, imp: self, - "Initializing GL for x11 GLX backend and display." + "Initializing GL for x11 GLX backend and display" ); let platform = gst_gl::GLPlatform::GLX; @@ -819,37 +735,36 @@ impl PaintableSink { let gl_ctx = gst_gl::GLContext::current_gl_context(platform); if gl_ctx == 0 { - gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext",); - return; + gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext"); + return None; } // FIXME: bindings unsafe { use glib::translate::*; - let d = display.downcast::().unwrap(); - let x11_display = gdk_x11::ffi::gdk_x11_display_get_xdisplay(d.to_glib_none().0); + let display = display.downcast::().unwrap(); + let x11_display = gdk_x11::ffi::gdk_x11_display_get_xdisplay(display.to_glib_none().0); if x11_display.is_null() { gst::error!(CAT, imp: self, "Failed to get X11 display"); - return; + return None; } let gst_display = gst_gl_x11::ffi::gst_gl_display_x11_new_with_display(x11_display); let gst_display = gst_gl::GLDisplay::from_glib_full(gst_display as *mut gst_gl::ffi::GstGLDisplay); - let gst_app_context = + let wrapped_context = gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api); - let gst_app_context = match gst_app_context { + let wrapped_context = match wrapped_context { None => { gst::error!(CAT, imp: self, "Failed to create wrapped GL context"); - return; + return None; } - Some(gst_app_context) => gst_app_context, + Some(wrapped_context) => wrapped_context, }; - display_guard.replace(gst_display); - app_ctx_guard.replace(gst_app_context); + Some((gst_display, wrapped_context)) } } @@ -857,13 +772,11 @@ impl PaintableSink { fn initialize_waylandegl( &self, display: gdk::Display, - display_guard: &mut Option, - app_ctx_guard: &mut Option, - ) { + ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> { gst::info!( CAT, imp: self, - "Initializing GL for Wayland EGL backend and display." + "Initializing GL for Wayland EGL backend and display" ); let platform = gst_gl::GLPlatform::EGL; @@ -871,8 +784,8 @@ impl PaintableSink { let gl_ctx = gst_gl::GLContext::current_gl_context(platform); if gl_ctx == 0 { - gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext",); - return; + gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext"); + return None; } // FIXME: bindings @@ -881,12 +794,12 @@ impl PaintableSink { // let wayland_display = gdk_wayland::WaylandDisplay::wl_display(display.downcast()); // get the ptr directly since we are going to use it raw - let d = display.downcast::().unwrap(); + let display = display.downcast::().unwrap(); let wayland_display = - gdk_wayland::ffi::gdk_wayland_display_get_wl_display(d.to_glib_none().0); + gdk_wayland::ffi::gdk_wayland_display_get_wl_display(display.to_glib_none().0); if wayland_display.is_null() { gst::error!(CAT, imp: self, "Failed to get Wayland display"); - return; + return None; } let gst_display = @@ -894,19 +807,18 @@ impl PaintableSink { let gst_display = gst_gl::GLDisplay::from_glib_full(gst_display as *mut gst_gl::ffi::GstGLDisplay); - let gst_app_context = + let wrapped_context = gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api); - let gst_app_context = match gst_app_context { + let wrapped_context = match wrapped_context { None => { gst::error!(CAT, imp: self, "Failed to create wrapped GL context"); - return; + return None; } - Some(gst_app_context) => gst_app_context, + Some(wrapped_context) => wrapped_context, }; - display_guard.replace(gst_display); - app_ctx_guard.replace(gst_app_context); + Some((gst_display, wrapped_context)) } } @@ -914,13 +826,11 @@ impl PaintableSink { fn initialize_macosgl( &self, display: gdk::Display, - display_guard: &mut Option, - app_ctx_guard: &mut Option, - ) { + ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> { gst::info!( CAT, imp: self, - "Initializing GL for macOS backend and display." + "Initializing GL for macOS backend and display" ); let platform = gst_gl::GLPlatform::CGL; @@ -928,25 +838,24 @@ impl PaintableSink { let gl_ctx = gst_gl::GLContext::current_gl_context(platform); if gl_ctx == 0 { - gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext",); - return; + gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext"); + return None; } let gst_display = gst_gl::GLDisplay::new(); unsafe { - let gst_app_context = + let wrapped_context = gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api); - let gst_app_context = match gst_app_context { + let wrapped_context = match wrapped_context { None => { gst::error!(CAT, imp: self, "Failed to create wrapped GL context"); return; } - Some(gst_app_context) => gst_app_context, + Some(wrapped_context) => wrapped_context, }; - display_guard.replace(gst_display); - app_ctx_guard.replace(gst_app_context); + Some((gst_display, wrapped_context)) } } } diff --git a/video/gtk4/src/sink/paintable/imp.rs b/video/gtk4/src/sink/paintable/imp.rs index 2b83e26f..eddfb88e 100644 --- a/video/gtk4/src/sink/paintable/imp.rs +++ b/video/gtk4/src/sink/paintable/imp.rs @@ -165,11 +165,6 @@ impl PaintableImpl for Paintable { } impl Paintable { - #[cfg(any(target_os = "macos", feature = "gst_gl"))] - pub(super) fn context(&self) -> Option { - self.gl_context.borrow().clone() - } - pub(super) fn handle_frame_changed(&self, frame: Option) { let context = self.gl_context.borrow(); if let Some(frame) = frame { @@ -191,9 +186,7 @@ impl Paintable { gst::debug!( CAT, imp: self, - "Size changed from {:?} to {:?}", - old_size, - new_size, + "Size changed from {old_size:?} to {new_size:?}", ); self.obj().invalidate_size(); } diff --git a/video/gtk4/src/sink/paintable/mod.rs b/video/gtk4/src/sink/paintable/mod.rs index 7def368d..835c43de 100644 --- a/video/gtk4/src/sink/paintable/mod.rs +++ b/video/gtk4/src/sink/paintable/mod.rs @@ -30,11 +30,6 @@ impl Paintable { } impl Paintable { - #[cfg(any(target_os = "macos", feature = "gst_gl"))] - pub(crate) fn context(&self) -> Option { - self.imp().context() - } - pub(crate) fn handle_frame_changed(&self, frame: Option) { self.imp().handle_frame_changed(frame); }