From 4d2c574cd3f65e1ac8664c29ff717ee4aa215de0 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Tue, 13 Aug 2024 18:23:07 +0530 Subject: [PATCH] [feat] refactored dmabuf->eglImage API into libwayshot --- Cargo.lock | 5 +- libwayshot/Cargo.toml | 6 +- libwayshot/examples/waymirror-egl/Cargo.toml | 2 +- .../examples/waymirror-egl/src/state.rs | 131 ++---------------- libwayshot/src/dispatch.rs | 26 ++++ libwayshot/src/error.rs | 6 +- libwayshot/src/lib.rs | 100 +++++++++---- 7 files changed, 119 insertions(+), 157 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ffd3fda3..69ca673d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,7 +567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading", + "pkg-config", ] [[package]] @@ -604,7 +604,10 @@ version = "0.3.2-dev" dependencies = [ "drm", "gbm", + "gl", + "gl_loader", "image", + "khronos-egl", "memmap2", "nix 0.27.1", "thiserror", diff --git a/libwayshot/Cargo.toml b/libwayshot/Cargo.toml index 12883678..991747be 100644 --- a/libwayshot/Cargo.toml +++ b/libwayshot/Cargo.toml @@ -20,4 +20,8 @@ wayland-protocols = { version = "0.31.0", features = ["client", "unstable"] } wayland-protocols-wlr = { version = "0.2.0", features = ["client"] } gbm = "0.15.0" -drm = "0.12.0" \ No newline at end of file +drm = "0.12.0" + +gl = "0.14.0" +gl_loader = "0.1.2" +khronos-egl = { version = "6.0.0",features = ["static"] } \ No newline at end of file diff --git a/libwayshot/examples/waymirror-egl/Cargo.toml b/libwayshot/examples/waymirror-egl/Cargo.toml index 4653f73d..eb6d9dc8 100644 --- a/libwayshot/examples/waymirror-egl/Cargo.toml +++ b/libwayshot/examples/waymirror-egl/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] gl = "0.14.0" gl_loader = "0.1.2" -khronos-egl = { version = "6.0.0",features = ["dynamic"] } +khronos-egl = { version = "6.0.0",features = ["static"] } thiserror = "1.0.58" tracing = "0.1.40" tracing-subscriber = "0.3.18" diff --git a/libwayshot/examples/waymirror-egl/src/state.rs b/libwayshot/examples/waymirror-egl/src/state.rs index c73b0790..1f7afe3a 100644 --- a/libwayshot/examples/waymirror-egl/src/state.rs +++ b/libwayshot/examples/waymirror-egl/src/state.rs @@ -4,8 +4,7 @@ use crate::utils::load_shader; use libwayshot::WayshotConnection; use gl::types::{GLeglImageOES, GLuint}; -use khronos_egl::{self as egl, Attrib, DynamicInstance, EGLClientBuffer}; -use std::os::fd::{AsRawFd, IntoRawFd}; +use khronos_egl::{self as egl}; use std::{ffi::c_void, rc::Rc}; use wayland_client::{ protocol::{wl_compositor, wl_display::WlDisplay, wl_surface::WlSurface}, @@ -25,7 +24,7 @@ pub struct WaylandEGLState { pub wl_display: WlDisplay, pub wl_surface: Option, - pub egl: egl::DynamicInstance, + pub egl: egl::Instance, pub egl_window: Option>, pub egl_display: Option, pub egl_surface: Option, @@ -47,12 +46,6 @@ impl WaylandEGLState { #[tracing::instrument] pub fn new() -> Result { let server_connection = Connection::connect_to_env()?; - let lib = - unsafe { libloading::Library::new("libEGL.so.1").expect("unable to find libEGL.so.1") }; - let egl = unsafe { - egl::DynamicInstance::::load_required_from(lib) - .expect("unable to load libEGL.so.1") - }; Ok(Self { width: 1920, @@ -64,7 +57,7 @@ impl WaylandEGLState { wl_display: server_connection.display(), wl_surface: None, - egl: egl, + egl: khronos_egl::Instance::new(egl::Static), egl_window: None, egl_display: None, egl_surface: None, @@ -136,7 +129,6 @@ impl WaylandEGLState { .egl .choose_first_config(self.egl_display.unwrap(), &attributes)? .expect("unable to find an appropriate EGL configuration"); - dbg!(config); self.egl_surface = Some(unsafe { self.egl.create_window_surface( self.egl_display.unwrap(), @@ -222,12 +214,11 @@ impl WaylandEGLState { unsafe { gl::GenTextures(1, &mut self.gl_texture); - self.dmabuf_to_egl(); + self.dmabuf_to_texture(); gl::GenVertexArrays(1, &mut vao as *mut u32); gl::GenBuffers(1, &mut vbo as *mut u32); gl::GenBuffers(1, &mut ebo as *mut u32); - dbg!(vbo, vao, ebo); gl::BindVertexArray(vao); gl::BindBuffer(gl::ARRAY_BUFFER, vbo); @@ -278,7 +269,7 @@ impl WaylandEGLState { gl::ClearColor(1.0, 1.0, 0.0, 1.0); gl::Clear(gl::COLOR_BUFFER_BIT); gl::DeleteTextures(1, &mut self.gl_texture); - self.dmabuf_to_egl(); + self.dmabuf_to_texture(); gl::UseProgram(self.gl_program); //gl::BindTexture(gl::TEXTURE_2D, self.gl_texture); //gl::BindVertexArray(1); @@ -286,83 +277,12 @@ impl WaylandEGLState { } } - pub fn dmabuf_to_egl(&self) { - type Attrib = egl::Attrib; - let (frame_format, guard, bo) = self + pub fn dmabuf_to_texture(&self) { + let image = self .wayshot - .capture_output_frame_dmabuf(true, &self.wayshot.get_all_outputs()[0].wl_output, None) + .capture_output_frame_eglimage(true, &self.wayshot.get_all_outputs()[0].wl_output, None) .unwrap(); - self.egl_window.as_ref().unwrap().resize( - frame_format.size.width as i32, - frame_format.size.height as i32, - 0, - 0, - ); - unsafe { - gl::Viewport( - 0, - 0, - frame_format.size.width as i32, - frame_format.size.height as i32, - ) - }; - // let modifier: u64 = dbg!(bo.modifier().unwrap().into()); - // dbg!(bo.plane_count().unwrap()); - let image_attribs = [ - egl::WIDTH as Attrib, - frame_format.size.width as Attrib, - egl::HEIGHT as Attrib, - frame_format.size.height as Attrib, - 0x3271, //EGL_LINUX_DRM_FOURCC_EXT - bo.format().unwrap() as Attrib, - 0x3272, //EGL_DMA_BUF_PLANE0_FD_EXT - bo.fd_for_plane(0).unwrap().into_raw_fd() as Attrib, - 0x3273, //EGL_DMA_BUF_PLANE0_OFFSET_EXT - bo.offset(0).unwrap() as Attrib, - 0x3274, //EGL_DMA_BUF_PLANE0_PITCH_EXT - bo.stride_for_plane(0).unwrap() as Attrib, - // 0x3443, //EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT - // (modifier as u32) as Attrib, - // 0x3444, //EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT - // (modifier >> 32) as Attrib, - egl::ATTRIB_NONE as Attrib, - ]; - - // self.wl_surface - // .as_ref() - // .unwrap() - // .attach(Some(&guard.buffer), 0, 0); - // self.wl_surface.as_ref().unwrap().commit(); - // let wlbuf = guard.buffer.id().as_ptr(); - // dbg!(image_attribs, image_attribs.len()); unsafe { - // let egl_create_image_khr: unsafe extern "system" fn( - // display: khronos_egl::EGLDisplay, - // ctx: khronos_egl::EGLContext, - // target: khronos_egl::Enum, - // buffer: khronos_egl::EGLClientBuffer, - // attrib_list: *const Attrib, - // ) - // -> khronos_egl::EGLImage = - // std::mem::transmute(dbg!(self.egl.get_proc_address("eglCreateImage").unwrap())); - // let image = egl_create_image_khr( - // self.egl_display.unwrap().as_ptr(), - // egl::NO_CONTEXT, - // 0x3270, - // std::ptr::null_mut() as EGLClientBuffer, - // image_attribs.as_ptr(), - // ); - let image = self - .egl - .create_image( - self.egl_display.unwrap(), - khronos_egl::Context::from_ptr(egl::NO_CONTEXT), - 0x3270, // EGL_LINUX_DMA_BUF_EXT - khronos_egl::ClientBuffer::from_ptr(std::ptr::null_mut()), //NULL - &image_attribs, - ) - .unwrap(); - //dbg!(image, self.egl.get_error()); let gl_egl_image_texture_target_2d_oes: unsafe extern "system" fn( target: gl::types::GLenum, image: GLeglImageOES, @@ -380,42 +300,7 @@ impl WaylandEGLState { self.egl .destroy_image(self.egl_display.unwrap(), image) .unwrap(); - - // let image_wl_attribs = [ - // 0x31D6 as Attrib, //EGL_WAYLAND_PLANE_WL - // 0 as Attrib, - // egl::ATTRIB_NONE as Attrib, - // ]; - // let image = egl_create_image_khr( - // self.egl_display.unwrap().as_ptr(), - // egl::NO_CONTEXT as *mut c_void, - // 0x31D5, //EGL_WAYLAND_BUFFER_WL, - // wlbuf as *mut c_void, - // image_wl_attribs.as_ptr(), - // ); - // dbg!(image, self.egl.get_error()); - //assert_ne!(image, 0 as *mut c_void); - // self.egl - // .create_image( - // self.egl_display.unwrap(), - // khronos_egl::Context::from_ptr(egl::NO_CONTEXT), - // 0x31D5, //EGL_WAYLAND_BUFFER_WL, - // khronos_egl::ClientBuffer::from_ptr(wlbuf.display_ptr() as *mut c_void), //NULL - // &image_wl_attribs, - // ) - // .unwrap(); } - // self.egl_image = Some(unsafe { - // self.egl - // .create_image( - // self.egl_display.unwrap(), - // khronos_egl::Context::from_ptr(egl::NO_CONTEXT), - // 0x3270, // EGL_LINUX_DMA_BUF_EXT - // ClientBuffer::from_ptr(std::ptr::null_mut()), //NULL - // &image_attribs, - // ) - // .unwrap() - // }); } pub fn validate_globals(&self) -> Result<()> { diff --git a/libwayshot/src/dispatch.rs b/libwayshot/src/dispatch.rs index 1ad6842c..e171cd1b 100644 --- a/libwayshot/src/dispatch.rs +++ b/libwayshot/src/dispatch.rs @@ -1,5 +1,6 @@ use std::{ collections::HashSet, + os::fd::{AsFd, BorrowedFd}, sync::atomic::{AtomicBool, Ordering}, }; use wayland_client::{ @@ -327,3 +328,28 @@ impl wayland_client::Dispatch for LayerShellState } } } +pub(crate) struct Card(std::fs::File); + +/// Implementing [`AsFd`] is a prerequisite to implementing the traits found +/// in this crate. Here, we are just calling [`File::as_fd()`] on the inner +/// [`File`]. +impl AsFd for Card { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +impl drm::Device for Card {} +/// Simple helper methods for opening a `Card`. +impl Card { + pub fn open(path: &str) -> Self { + let mut options = std::fs::OpenOptions::new(); + options.read(true); + options.write(true); + Card(options.open(path).unwrap()) + } +} +#[derive(Debug)] +pub(crate) struct DMABUFState { + pub linux_dmabuf: ZwpLinuxDmabufV1, + pub gbmdev: gbm::Device, +} diff --git a/libwayshot/src/error.rs b/libwayshot/src/error.rs index 0fa36976..f01c42c8 100644 --- a/libwayshot/src/error.rs +++ b/libwayshot/src/error.rs @@ -40,8 +40,10 @@ pub enum Error { NoDMAStateError, #[error("dmabuf color format provided by compositor is invalid")] UnrecognizedColorCode(#[from] UnrecognizedFourcc), - #[error("dmabuf device is destroyed")] + #[error("dmabuf device has been destroyed")] DRMDeviceLost(#[from] DeviceDestroyedError), - #[error("obtaining gbm buffer object file descriptor failed")] + #[error("obtaining gbm buffer object file descriptor failed {0}")] GBMBoFdError(#[from] FdError), + #[error(" EGLImage import from dmabuf failed: {0}")] + EGLError(#[from] khronos_egl::Error), } diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index 9fc6c86d..502a3c8a 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -13,14 +13,16 @@ mod screencopy; use std::{ collections::HashSet, + ffi::c_void, fs::File, - os::fd::{AsFd, BorrowedFd, OwnedFd}, + os::fd::{AsFd, IntoRawFd, OwnedFd}, sync::atomic::{AtomicBool, Ordering}, thread, }; -use dispatch::LayerShellState; +use dispatch::{DMABUFState, LayerShellState}; use image::{imageops::replace, DynamicImage}; +use khronos_egl::{self as egl, Instance}; use memmap2::MmapMut; use region::{EmbeddedRegion, RegionCapturer}; use screencopy::{DMAFrameFormat, DMAFrameGuard, FrameData, FrameGuard}; @@ -68,32 +70,6 @@ pub mod reexport { pub use wl_output::{Transform, WlOutput}; } use gbm::{BufferObject, BufferObjectFlags, Device as GBMDevice}; -struct Card(std::fs::File); - -/// Implementing [`AsFd`] is a prerequisite to implementing the traits found -/// in this crate. Here, we are just calling [`File::as_fd()`] on the inner -/// [`File`]. -impl AsFd for Card { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} -impl drm::Device for Card {} -/// Simple helper methods for opening a `Card`. -impl Card { - pub fn open(path: &str) -> Self { - let mut options = std::fs::OpenOptions::new(); - options.read(true); - options.write(true); - Card(options.open(path).unwrap()) - } -} - -#[derive(Debug)] -struct DMABUFState { - linux_dmabuf: ZwpLinuxDmabufV1, - gbmdev: GBMDevice, -} /// Struct to store wayland connection and globals list. /// # Example usage @@ -137,7 +113,7 @@ impl WayshotConnection { let (globals, evq) = registry_queue_init::(&conn)?; let linux_dmabuf = globals.bind(&evq.handle(), 4..=ZwpLinuxDmabufV1::interface().version, ())?; - let gpu = Card::open("/dev/dri/renderD128"); + let gpu = dispatch::Card::open("/dev/dri/renderD128"); // init a GBM device let gbm = GBMDevice::new(gpu).unwrap(); let mut initial_state = Self { @@ -246,6 +222,72 @@ impl WayshotConnection { Ok((frame_format, frame_guard)) } + pub fn capture_output_frame_eglimage( + &self, + cursor_overlay: bool, + output: &WlOutput, + capture_region: Option, + ) -> Result { + let egl = khronos_egl::Instance::new(egl::Static); + let egl_display = unsafe { + match egl.get_display(self.conn.display().id().as_ptr() as *mut c_void) { + Some(disp) => disp, + None => return Err(egl.get_error().unwrap().into()), + } + }; + + egl.initialize(egl_display)?; + self.capture_output_frame_eglimage_on_display( + &egl, + egl_display, + cursor_overlay, + output, + capture_region, + ) + } + pub fn capture_output_frame_eglimage_on_display( + &self, + egl_instance: &Instance, + egl_display: egl::Display, + cursor_overlay: bool, + output: &WlOutput, + capture_region: Option, + ) -> Result { + type Attrib = egl::Attrib; + let (frame_format, _guard, bo) = + self.capture_output_frame_dmabuf(cursor_overlay, output, capture_region)?; + let image_attribs = [ + egl::WIDTH as Attrib, + frame_format.size.width as Attrib, + egl::HEIGHT as Attrib, + frame_format.size.height as Attrib, + 0x3271, //EGL_LINUX_DRM_FOURCC_EXT + bo.format().unwrap() as Attrib, + 0x3272, //EGL_DMA_BUF_PLANE0_FD_EXT + bo.fd_for_plane(0).unwrap().into_raw_fd() as Attrib, + 0x3273, //EGL_DMA_BUF_PLANE0_OFFSET_EXT + bo.offset(0).unwrap() as Attrib, + 0x3274, //EGL_DMA_BUF_PLANE0_PITCH_EXT + bo.stride_for_plane(0).unwrap() as Attrib, + // 0x3443, //EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT + // (modifier as u32) as Attrib, + // 0x3444, //EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT + // (modifier >> 32) as Attrib, + egl::ATTRIB_NONE as Attrib, + ]; + unsafe { + match egl_instance.create_image( + egl_display, + khronos_egl::Context::from_ptr(egl::NO_CONTEXT), + 0x3270, // EGL_LINUX_DMA_BUF_EXT + khronos_egl::ClientBuffer::from_ptr(std::ptr::null_mut()), //NULL + &image_attribs, + ) { + Ok(image) => Ok(image), + Err(e) => Err(e.into()), + } + } + } /// Obtain a screencapture in the form of a WlBuffer backed by a GBM Bufferobject on the GPU. /// Uses the dma-buf provisions of the wlr-screencopy copy protocol to avoid VRAM->RAM copies