From de15b6f52c38ab15981bb96c1c06c82a99309926 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Thu, 23 May 2024 13:44:22 +0530 Subject: [PATCH 01/21] [feat] rough MVP for screencpy+dmabuf --- Cargo.lock | 118 ++++++++++++++++++++++++++++++++++++- libwayshot/Cargo.toml | 3 + libwayshot/src/dispatch.rs | 65 +++++++++++++++++--- libwayshot/src/lib.rs | 109 ++++++++++++++++++++++++++++++++-- 4 files changed, 282 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 04fcbd3d..2ac859fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,6 +100,20 @@ name = "bytemuck" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "byteorder" @@ -263,6 +277,45 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "drm" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" +dependencies = [ + "bitflags 2.5.0", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "rustix", +] + +[[package]] +name = "drm-ffi" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53" +dependencies = [ + "drm-sys", + "rustix", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986" +dependencies = [ + "libc", + "linux-raw-sys 0.6.4", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -341,6 +394,30 @@ dependencies = [ "thread_local", ] +[[package]] +name = "gbm" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bf55ba6dd53ad0ac115046ff999c5324c283444ee6e0be82454c4e8eb2f36a" +dependencies = [ + "bitflags 2.5.0", + "drm", + "drm-fourcc", + "gbm-sys", + "libc", + "wayland-backend", + "wayland-server", +] + +[[package]] +name = "gbm-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd2d6bf7c0143b38beece05f9a5c4c851a49a8434f62bf58ff28da92b0ddc58" +dependencies = [ + "libc", +] + [[package]] name = "glob" version = "0.3.1" @@ -423,6 +500,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-lifetimes" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" + [[package]] name = "jobserver" version = "0.1.28" @@ -473,6 +556,8 @@ dependencies = [ name = "libwayshot" version = "0.3.2-dev" dependencies = [ + "drm", + "gbm", "image", "memmap2", "nix 0.27.1", @@ -499,6 +584,12 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "linux-raw-sys" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4" + [[package]] name = "log" version = "0.4.21" @@ -520,6 +611,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -690,7 +790,7 @@ dependencies = [ "bitflags 2.5.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.13", "windows-sys", ] @@ -997,6 +1097,20 @@ dependencies = [ "quote", ] +[[package]] +name = "wayland-server" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e6e4d5c285bc24ba4ed2d5a4bd4febd5fd904451f465973225c8e99772fdb7" +dependencies = [ + "bitflags 2.5.0", + "downcast-rs", + "io-lifetimes", + "rustix", + "wayland-backend", + "wayland-scanner", +] + [[package]] name = "wayland-sys" version = "0.31.1" @@ -1004,7 +1118,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" dependencies = [ "dlib", + "libc", "log", + "memoffset", "pkg-config", ] diff --git a/libwayshot/Cargo.toml b/libwayshot/Cargo.toml index 8a9719b0..12883678 100644 --- a/libwayshot/Cargo.toml +++ b/libwayshot/Cargo.toml @@ -18,3 +18,6 @@ thiserror = "1" wayland-client = "0.31.1" 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 diff --git a/libwayshot/src/dispatch.rs b/libwayshot/src/dispatch.rs index c8f21fcf..3ccffe28 100644 --- a/libwayshot/src/dispatch.rs +++ b/libwayshot/src/dispatch.rs @@ -6,15 +6,26 @@ use wayland_client::{ delegate_noop, globals::GlobalListContents, protocol::{ - wl_buffer::WlBuffer, wl_compositor::WlCompositor, wl_output, wl_output::WlOutput, - wl_registry, wl_registry::WlRegistry, wl_shm::WlShm, wl_shm_pool::WlShmPool, + wl_buffer::WlBuffer, + wl_compositor::WlCompositor, + wl_output::{self, WlOutput}, + wl_registry::{self, WlRegistry}, + wl_shm::WlShm, + wl_shm_pool::WlShmPool, wl_surface::WlSurface, }, - Connection, Dispatch, QueueHandle, WEnum, - WEnum::Value, + Connection, Dispatch, Proxy, QueueHandle, + WEnum::{self, Value}, }; -use wayland_protocols::xdg::xdg_output::zv1::client::{ - zxdg_output_manager_v1::ZxdgOutputManagerV1, zxdg_output_v1, zxdg_output_v1::ZxdgOutputV1, +use wayland_protocols::{ + wp::linux_dmabuf::zv1::client::{ + zwp_linux_buffer_params_v1::{self, ZwpLinuxBufferParamsV1}, + zwp_linux_dmabuf_v1::{self, ZwpLinuxDmabufV1}, + }, + xdg::xdg_output::zv1::client::{ + zxdg_output_manager_v1::ZxdgOutputManagerV1, + zxdg_output_v1::{self, ZxdgOutputV1}, + }, }; use wayland_protocols_wlr::layer_shell::v1::client::{ zwlr_layer_shell_v1::ZwlrLayerShellV1, @@ -169,10 +180,44 @@ pub enum FrameState { pub struct CaptureFrameState { pub formats: Vec, + pub dmabuf_formats: Vec<(u32, u32, u32)>, pub state: Option, pub buffer_done: AtomicBool, } +impl Dispatch for CaptureFrameState { + fn event( + _state: &mut Self, + _proxy: &ZwpLinuxDmabufV1, + _event: zwp_linux_dmabuf_v1::Event, + _data: &(), + _conn: &Connection, + _qhandle: &wayland_client::QueueHandle, + ) { + } +} + +impl Dispatch for CaptureFrameState { + fn event( + _state: &mut Self, + _proxy: &ZwpLinuxBufferParamsV1, + event: zwp_linux_buffer_params_v1::Event, + _data: &(), + _conn: &Connection, + _qhandle: &QueueHandle, + ) { + match event { + zwp_linux_buffer_params_v1::Event::Created { buffer } => { + tracing::trace!("DMABUF Creation success") + } + zwp_linux_buffer_params_v1::Event::Failed => { + tracing::error!("DMABUF Creation failed") + } + _ => todo!(), + } + } +} + impl Dispatch for CaptureFrameState { #[tracing::instrument(skip(frame), ret, level = "trace")] fn event( @@ -209,7 +254,13 @@ impl Dispatch for CaptureFrameState { frame.state.replace(FrameState::Failed); } zwlr_screencopy_frame_v1::Event::Damage { .. } => {} - zwlr_screencopy_frame_v1::Event::LinuxDmabuf { .. } => {} + zwlr_screencopy_frame_v1::Event::LinuxDmabuf { + format, + width, + height, + } => { + frame.dmabuf_formats.push((format, width, height)); + } zwlr_screencopy_frame_v1::Event::BufferDone => { frame.buffer_done.store(true, Ordering::SeqCst); } diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index d0ec88ad..4a5aa9d4 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -14,14 +14,17 @@ mod screencopy; use std::{ collections::HashSet, fs::File, - os::fd::AsFd, + io::Read, + os::fd::{AsFd, AsRawFd, BorrowedFd}, sync::atomic::{AtomicBool, Ordering}, thread, }; use dispatch::LayerShellState; +use drm::Device; use image::{imageops::replace, DynamicImage}; use memmap2::MmapMut; +use nix::unistd::write; use region::{EmbeddedRegion, RegionCapturer}; use screencopy::FrameGuard; use tracing::debug; @@ -32,10 +35,16 @@ use wayland_client::{ wl_output::{Transform, WlOutput}, wl_shm::{self, WlShm}, }, - Connection, EventQueue, + Connection, EventQueue, Proxy, }; -use wayland_protocols::xdg::xdg_output::zv1::client::{ - zxdg_output_manager_v1::ZxdgOutputManagerV1, zxdg_output_v1::ZxdgOutputV1, +use wayland_protocols::{ + wp::linux_dmabuf::{ + self, + zv1::client::{zwp_linux_buffer_params_v1, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1}, + }, + xdg::xdg_output::zv1::client::{ + zxdg_output_manager_v1::ZxdgOutputManagerV1, zxdg_output_v1::ZxdgOutputV1, + }, }; use wayland_protocols_wlr::{ layer_shell::v1::client::{ @@ -62,6 +71,27 @@ pub mod reexport { use wayland_client::protocol::wl_output; pub use wl_output::{Transform, WlOutput}; } +use gbm::{BufferObjectFlags, Device as GBMDevice, Format}; +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()) + } +} /// Struct to store wayland connection and globals list. /// # Example usage @@ -186,6 +216,7 @@ impl WayshotConnection { )> { let mut state = CaptureFrameState { formats: Vec::new(), + dmabuf_formats: Vec::new(), state: None, buffer_done: AtomicBool::new(false), }; @@ -276,6 +307,46 @@ impl WayshotConnection { // Instantiate shm global. let shm = self.globals.bind::(&qh, 1..=1, ())?; + let linux_dmabuf: ZwpLinuxDmabufV1 = + self.globals + .bind(&qh, 4..=ZwpLinuxDmabufV1::interface().version, ())?; + let gpu = Card::open("/dev/dri/renderD128"); + println!("{:#?}", gpu.get_driver().unwrap()); + // init a GBM device + let gbm = GBMDevice::new(gpu).unwrap(); + // println!("{:#?}", gpu.get_driver().unwrap()); + let dma_width = state.dmabuf_formats[0].1; + let dma_height = state.dmabuf_formats[0].2; + // create a buffer + let bo = gbm + .create_buffer_object::<()>( + dma_width, + dma_height, + Format::Xbgr8888, + BufferObjectFlags::RENDERING | BufferObjectFlags::LINEAR, + ) + .unwrap(); + //dbg!(bo); + let dma_params = linux_dmabuf.create_params(&qh, ()); + dbg!(&dma_params); + let modifier: u64 = 72057594037927935; + let stride = bo.stride().unwrap(); + dma_params.add( + bo.fd().unwrap().as_fd(), + 0, + 0, + stride, + (modifier >> 32) as u32, + (modifier & 0xffffffff) as u32, + ); + let dmabuf = dma_params.create_immed( + dma_width as i32, + dma_height as i32, + state.dmabuf_formats[0].0, + zwp_linux_buffer_params_v1::Flags::empty(), + &qh, + (), + ); let shm_pool = shm.create_pool( fd.as_fd(), frame_format @@ -296,7 +367,23 @@ impl WayshotConnection { ); // Copy the pixel data advertised by the compositor into the buffer we just created. - frame.copy(&buffer); + frame.copy(&dmabuf); + dbg!("copy complete"); + let bobuffer = { + let mut bobuffer = Vec::new(); + for i in 0..1280 { + for _ in 0..720 { + bobuffer.push(if i % 2 == 0 { 0 } else { 255 }); + } + } + bobuffer + }; + + //dma_params.destroy(); + //linux_dmabuf.destroy(); + //dbg!(bobuffer); + dbg!(&state.dmabuf_formats); + dbg!(&state.formats); // On copy the Ready / Failed events are fired by the frame object, so here we check for them. loop { // Basically reads, if frame state is not None then... @@ -307,6 +394,18 @@ impl WayshotConnection { return Err(Error::FramecopyFailed); } FrameState::Finished => { + tracing::trace!("Frame copy finished"); + let mem = bo + .map(&gbm, 0, 0, dma_width, dma_height, |obj| { + obj.buffer().to_vec() + }) + .unwrap() + .unwrap(); + //dbg!(mem); + unsafe { + write(fd.as_fd().as_raw_fd(), mem.as_ref()).unwrap(); + } + return Ok(FrameGuard { buffer, shm_pool }); } } From 2cb5a04ccedf18d45f90c4840a5db1156e180ab0 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Sat, 1 Jun 2024 23:50:48 +0530 Subject: [PATCH 02/21] [feat] refactor MVP into libwayshot - create new constructor --- libwayshot/src/dispatch.rs | 13 +--- libwayshot/src/lib.rs | 138 +++++++++++-------------------------- 2 files changed, 44 insertions(+), 107 deletions(-) diff --git a/libwayshot/src/dispatch.rs b/libwayshot/src/dispatch.rs index 3ccffe28..2e7ee7f0 100644 --- a/libwayshot/src/dispatch.rs +++ b/libwayshot/src/dispatch.rs @@ -14,7 +14,7 @@ use wayland_client::{ wl_shm_pool::WlShmPool, wl_surface::WlSurface, }, - Connection, Dispatch, Proxy, QueueHandle, + Connection, Dispatch, QueueHandle, WEnum::{self, Value}, }; use wayland_protocols::{ @@ -206,15 +206,6 @@ impl Dispatch for CaptureFrameState { _conn: &Connection, _qhandle: &QueueHandle, ) { - match event { - zwp_linux_buffer_params_v1::Event::Created { buffer } => { - tracing::trace!("DMABUF Creation success") - } - zwp_linux_buffer_params_v1::Event::Failed => { - tracing::error!("DMABUF Creation failed") - } - _ => todo!(), - } } } @@ -277,7 +268,7 @@ delegate_noop!(CaptureFrameState: ignore ZwlrScreencopyManagerV1); // TODO: Create a xdg-shell surface, check for the enter event, grab the output from it. pub struct WayshotState {} - +delegate_noop!(WayshotState: ignore ZwpLinuxDmabufV1); impl wayland_client::Dispatch for WayshotState { fn event( _: &mut WayshotState, diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index 4a5aa9d4..e4140426 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -14,8 +14,7 @@ mod screencopy; use std::{ collections::HashSet, fs::File, - io::Read, - os::fd::{AsFd, AsRawFd, BorrowedFd}, + os::fd::{AsFd, BorrowedFd}, sync::atomic::{AtomicBool, Ordering}, thread, }; @@ -24,7 +23,6 @@ use dispatch::LayerShellState; use drm::Device; use image::{imageops::replace, DynamicImage}; use memmap2::MmapMut; -use nix::unistd::write; use region::{EmbeddedRegion, RegionCapturer}; use screencopy::FrameGuard; use tracing::debug; @@ -38,10 +36,7 @@ use wayland_client::{ Connection, EventQueue, Proxy, }; use wayland_protocols::{ - wp::linux_dmabuf::{ - self, - zv1::client::{zwp_linux_buffer_params_v1, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1}, - }, + wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, xdg::xdg_output::zv1::client::{ zxdg_output_manager_v1::ZxdgOutputManagerV1, zxdg_output_v1::ZxdgOutputV1, }, @@ -71,7 +66,7 @@ pub mod reexport { use wayland_client::protocol::wl_output; pub use wl_output::{Transform, WlOutput}; } -use gbm::{BufferObjectFlags, Device as GBMDevice, Format}; +use gbm::Device as GBMDevice; struct Card(std::fs::File); /// Implementing [`AsFd`] is a prerequisite to implementing the traits found @@ -93,6 +88,12 @@ impl Card { } } +#[derive(Debug)] +struct DMABUFState { + linux_dmabuf: ZwpLinuxDmabufV1, + gbmdev: GBMDevice, +} + /// Struct to store wayland connection and globals list. /// # Example usage /// @@ -105,6 +106,7 @@ pub struct WayshotConnection { pub conn: Connection, pub globals: GlobalList, output_infos: Vec, + dmabuf_state: Option, } impl WayshotConnection { @@ -122,6 +124,30 @@ impl WayshotConnection { conn, globals, output_infos: Vec::new(), + dmabuf_state: None, + }; + + initial_state.refresh_outputs()?; + + Ok(initial_state) + } + + pub fn from_connection_with_dmabuf(conn: Connection) -> Result { + 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"); + println!("{:#?}", gpu.get_driver().unwrap()); + // init a GBM device + let gbm = GBMDevice::new(gpu).unwrap(); + let mut initial_state = Self { + conn, + globals, + output_infos: Vec::new(), + dmabuf_state: Some(DMABUFState { + linux_dmabuf, + gbmdev: gbm, + }), }; initial_state.refresh_outputs()?; @@ -307,46 +333,6 @@ impl WayshotConnection { // Instantiate shm global. let shm = self.globals.bind::(&qh, 1..=1, ())?; - let linux_dmabuf: ZwpLinuxDmabufV1 = - self.globals - .bind(&qh, 4..=ZwpLinuxDmabufV1::interface().version, ())?; - let gpu = Card::open("/dev/dri/renderD128"); - println!("{:#?}", gpu.get_driver().unwrap()); - // init a GBM device - let gbm = GBMDevice::new(gpu).unwrap(); - // println!("{:#?}", gpu.get_driver().unwrap()); - let dma_width = state.dmabuf_formats[0].1; - let dma_height = state.dmabuf_formats[0].2; - // create a buffer - let bo = gbm - .create_buffer_object::<()>( - dma_width, - dma_height, - Format::Xbgr8888, - BufferObjectFlags::RENDERING | BufferObjectFlags::LINEAR, - ) - .unwrap(); - //dbg!(bo); - let dma_params = linux_dmabuf.create_params(&qh, ()); - dbg!(&dma_params); - let modifier: u64 = 72057594037927935; - let stride = bo.stride().unwrap(); - dma_params.add( - bo.fd().unwrap().as_fd(), - 0, - 0, - stride, - (modifier >> 32) as u32, - (modifier & 0xffffffff) as u32, - ); - let dmabuf = dma_params.create_immed( - dma_width as i32, - dma_height as i32, - state.dmabuf_formats[0].0, - zwp_linux_buffer_params_v1::Flags::empty(), - &qh, - (), - ); let shm_pool = shm.create_pool( fd.as_fd(), frame_format @@ -367,23 +353,7 @@ impl WayshotConnection { ); // Copy the pixel data advertised by the compositor into the buffer we just created. - frame.copy(&dmabuf); - dbg!("copy complete"); - let bobuffer = { - let mut bobuffer = Vec::new(); - for i in 0..1280 { - for _ in 0..720 { - bobuffer.push(if i % 2 == 0 { 0 } else { 255 }); - } - } - bobuffer - }; - - //dma_params.destroy(); - //linux_dmabuf.destroy(); - //dbg!(bobuffer); - dbg!(&state.dmabuf_formats); - dbg!(&state.formats); + frame.copy(&buffer); // On copy the Ready / Failed events are fired by the frame object, so here we check for them. loop { // Basically reads, if frame state is not None then... @@ -395,17 +365,6 @@ impl WayshotConnection { } FrameState::Finished => { tracing::trace!("Frame copy finished"); - let mem = bo - .map(&gbm, 0, 0, dma_width, dma_height, |obj| { - obj.buffer().to_vec() - }) - .unwrap() - .unwrap(); - //dbg!(mem); - unsafe { - write(fd.as_fd().as_raw_fd(), mem.as_ref()).unwrap(); - } - return Ok(FrameGuard { buffer, shm_pool }); } } @@ -490,26 +449,13 @@ impl WayshotConnection { output_capture_regions: &[(OutputInfo, Option)], cursor_overlay: bool, ) -> Result> { - let frame_copies = thread::scope(|scope| -> Result<_> { - let join_handles = output_capture_regions - .iter() - .map(|(output_info, capture_region)| { - scope.spawn(move || { - self.capture_frame_copy(cursor_overlay, output_info, *capture_region) - .map(|(frame_copy, frame_guard)| { - (frame_copy, frame_guard, output_info.clone()) - }) - }) - }) - .collect::>(); - - join_handles - .into_iter() - .flat_map(|join_handle| join_handle.join()) - .collect::>() - })?; - - Ok(frame_copies) + output_capture_regions + .iter() + .map(|(output_info, capture_region)| { + self.capture_frame_copy(cursor_overlay, output_info, *capture_region) + .map(|(frame_copy, frame_guard)| (frame_copy, frame_guard, output_info.clone())) + }) + .collect() } /// Create a layer shell surface for each output, From 06e07cba2e2f5cb391f65d348f9b6f9f6c394bf3 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Tue, 18 Jun 2024 10:50:08 +0530 Subject: [PATCH 03/21] [refactor] first draft of dmabuf API --- libwayshot/src/dispatch.rs | 17 ++- libwayshot/src/lib.rs | 274 +++++++++++++++++++++++++++++++---- libwayshot/src/screencopy.rs | 51 +++++-- 3 files changed, 300 insertions(+), 42 deletions(-) diff --git a/libwayshot/src/dispatch.rs b/libwayshot/src/dispatch.rs index 2e7ee7f0..1ad6842c 100644 --- a/libwayshot/src/dispatch.rs +++ b/libwayshot/src/dispatch.rs @@ -39,7 +39,7 @@ use wayland_protocols_wlr::screencopy::v1::client::{ use crate::{ output::OutputInfo, region::{LogicalRegion, Position, Size}, - screencopy::FrameFormat, + screencopy::{DMAFrameFormat, FrameFormat}, }; #[derive(Debug)] @@ -180,14 +180,14 @@ pub enum FrameState { pub struct CaptureFrameState { pub formats: Vec, - pub dmabuf_formats: Vec<(u32, u32, u32)>, + pub dmabuf_formats: Vec, pub state: Option, pub buffer_done: AtomicBool, } impl Dispatch for CaptureFrameState { fn event( - _state: &mut Self, + _frame: &mut Self, _proxy: &ZwpLinuxDmabufV1, _event: zwp_linux_dmabuf_v1::Event, _data: &(), @@ -201,7 +201,7 @@ impl Dispatch for CaptureFrameState { fn event( _state: &mut Self, _proxy: &ZwpLinuxBufferParamsV1, - event: zwp_linux_buffer_params_v1::Event, + _event: zwp_linux_buffer_params_v1::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, @@ -227,6 +227,7 @@ impl Dispatch for CaptureFrameState { stride, } => { if let Value(f) = format { + tracing::debug!("Received Buffer event with format: {f:?}"); frame.formats.push(FrameFormat { format: f, size: Size { width, height }, @@ -250,7 +251,13 @@ impl Dispatch for CaptureFrameState { width, height, } => { - frame.dmabuf_formats.push((format, width, height)); + tracing::debug!( + "Received wlr-screencopy linux_dmabuf event with format: {format} and size {width}x{height}" + ); + frame.dmabuf_formats.push(DMAFrameFormat { + format, + size: Size { width, height }, + }); } zwlr_screencopy_frame_v1::Event::BufferDone => { frame.buffer_done.store(true, Ordering::SeqCst); diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index e4140426..13463a13 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -14,17 +14,16 @@ mod screencopy; use std::{ collections::HashSet, fs::File, - os::fd::{AsFd, BorrowedFd}, + os::fd::{AsFd, BorrowedFd, OwnedFd}, sync::atomic::{AtomicBool, Ordering}, thread, }; use dispatch::LayerShellState; -use drm::Device; use image::{imageops::replace, DynamicImage}; use memmap2::MmapMut; use region::{EmbeddedRegion, RegionCapturer}; -use screencopy::FrameGuard; +use screencopy::{DMAFrameFormat, DMAFrameGuard, FrameData, FrameGuard}; use tracing::debug; use wayland_client::{ globals::{registry_queue_init, GlobalList}, @@ -36,7 +35,9 @@ use wayland_client::{ Connection, EventQueue, Proxy, }; use wayland_protocols::{ - wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, + wp::linux_dmabuf::zv1::client::{ + zwp_linux_buffer_params_v1, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, + }, xdg::xdg_output::zv1::client::{ zxdg_output_manager_v1::ZxdgOutputManagerV1, zxdg_output_v1::ZxdgOutputV1, }, @@ -66,7 +67,7 @@ pub mod reexport { use wayland_client::protocol::wl_output; pub use wl_output::{Transform, WlOutput}; } -use gbm::Device as GBMDevice; +use gbm::{BufferObject, BufferObjectFlags, Device as GBMDevice}; struct Card(std::fs::File); /// Implementing [`AsFd`] is a prerequisite to implementing the traits found @@ -137,7 +138,6 @@ impl WayshotConnection { let linux_dmabuf = globals.bind(&evq.handle(), 4..=ZwpLinuxDmabufV1::interface().version, ())?; let gpu = Card::open("/dev/dri/renderD128"); - println!("{:#?}", gpu.get_driver().unwrap()); // init a GBM device let gbm = GBMDevice::new(gpu).unwrap(); let mut initial_state = Self { @@ -222,14 +222,74 @@ impl WayshotConnection { capture_region: Option, ) -> Result<(FrameFormat, FrameGuard)> { let (state, event_queue, frame, frame_format) = - self.capture_output_frame_get_state(cursor_overlay, output, capture_region)?; + self.capture_output_frame_get_state_shm(cursor_overlay, output, capture_region)?; let frame_guard = self.capture_output_frame_inner(state, event_queue, frame, frame_format, fd)?; Ok((frame_format, frame_guard)) } - fn capture_output_frame_get_state( + fn capture_output_frame_shm_from_file( + &self, + cursor_overlay: bool, + output: &WlOutput, + file: &File, + capture_region: Option, + ) -> Result<(FrameFormat, FrameGuard)> { + let (state, event_queue, frame, frame_format) = + self.capture_output_frame_get_state_shm(cursor_overlay as i32, output, capture_region)?; + + file.set_len(frame_format.byte_size())?; + + let frame_guard = + self.capture_output_frame_inner(state, event_queue, frame, frame_format, file)?; + + Ok((frame_format, frame_guard)) + } + + pub fn capture_output_frame_dmabuf( + &self, + cursor_overlay: bool, + output: &WlOutput, + capture_region: Option, + ) -> Result<(DMAFrameFormat, DMAFrameGuard, BufferObject<()>)> { + match &self.dmabuf_state { + Some(dmabuf_state) => { + let (state, event_queue, frame, frame_format) = self + .capture_output_frame_get_state_dmabuf( + cursor_overlay as i32, + output, + capture_region, + )?; + let gbm = &dmabuf_state.gbmdev; + let bo = gbm + .create_buffer_object::<()>( + frame_format.size.width, + frame_format.size.height, + gbm::Format::try_from(frame_format.format).unwrap(), + BufferObjectFlags::RENDERING | BufferObjectFlags::LINEAR, + ) + .unwrap(); + let stride = bo.stride().unwrap(); + let modifier: u64 = bo.modifier().unwrap().into(); + + let frame_guard = self.capture_output_frame_inner_dmabuf( + state, + event_queue, + frame, + frame_format, + stride, + modifier, + bo.fd().unwrap(), + )?; + + Ok((frame_format, frame_guard, bo)) + } + None => todo!(), + } + } + + fn capture_output_frame_get_state_shm( &self, cursor_overlay: i32, output: &WlOutput, @@ -320,6 +380,144 @@ impl WayshotConnection { Ok((state, event_queue, frame, frame_format)) } + fn capture_output_frame_get_state_dmabuf( + &self, + cursor_overlay: i32, + output: &WlOutput, + capture_region: Option, + ) -> Result<( + CaptureFrameState, + EventQueue, + ZwlrScreencopyFrameV1, + DMAFrameFormat, + )> { + let mut state = CaptureFrameState { + formats: Vec::new(), + dmabuf_formats: Vec::new(), + state: None, + buffer_done: AtomicBool::new(false), + }; + let mut event_queue = self.conn.new_event_queue::(); + let qh = event_queue.handle(); + + // Instantiating screencopy manager. + let screencopy_manager = match self.globals.bind::( + &qh, + 3..=3, + (), + ) { + Ok(x) => x, + Err(e) => { + tracing::error!("Failed to create screencopy manager. Does your compositor implement ZwlrScreencopy?"); + tracing::error!("err: {e}"); + return Err(Error::ProtocolNotFound( + "ZwlrScreencopy Manager not found".to_string(), + )); + } + }; + + debug!("Capturing output..."); + let frame = if let Some(embedded_region) = capture_region { + screencopy_manager.capture_output_region( + cursor_overlay, + output, + embedded_region.inner.position.x, + embedded_region.inner.position.y, + embedded_region.inner.size.width as i32, + embedded_region.inner.size.height as i32, + &qh, + (), + ) + } else { + screencopy_manager.capture_output(cursor_overlay, output, &qh, ()) + }; + + // Empty internal event buffer until buffer_done is set to true which is when the Buffer done + // event is fired, aka the capture from the compositor is succesful. + while !state.buffer_done.load(Ordering::SeqCst) { + event_queue.blocking_dispatch(&mut state)?; + } + + tracing::trace!( + "Received compositor frame buffer formats: {:#?}", + state.formats + ); + // TODO select appropriate format if there is more than one + let frame_format = state.dmabuf_formats[0]; + tracing::trace!("Selected frame buffer format: {:#?}", frame_format); + + Ok((state, event_queue, frame, frame_format)) + } + + fn capture_output_frame_inner_dmabuf( + &self, + mut state: CaptureFrameState, + mut event_queue: EventQueue, + frame: ZwlrScreencopyFrameV1, + frame_format: DMAFrameFormat, + stride: u32, + modifier: u64, + fd: OwnedFd, + ) -> Result { + match &self.dmabuf_state { + Some(dmabuf_state) => { + // Connecting to wayland environment. + let qh = event_queue.handle(); + + let linux_dmabuf = &dmabuf_state.linux_dmabuf; + let dma_width = frame_format.size.width; + let dma_height = frame_format.size.height; + + let dma_params = linux_dmabuf.create_params(&qh, ()); + dma_params.add( + fd.as_fd(), + 0, + 0, + stride, + (modifier >> 32) as u32, + (modifier & 0xffffffff) as u32, + ); + let dmabuf_wlbuf = dma_params.create_immed( + dma_width as i32, + dma_height as i32, + frame_format.format, + zwp_linux_buffer_params_v1::Flags::empty(), + &qh, + (), + ); + + // Copy the pixel data advertised by the compositor into the buffer we just created. + frame.copy(&dmabuf_wlbuf); + tracing::debug!("wlr-screencopy copy() with dmabuf complete"); + + //dma_params.destroy(); + //linux_dmabuf.destroy(); + // On copy the Ready / Failed events are fired by the frame object, so here we check for them. + loop { + // Basically reads, if frame state is not None then... + if let Some(state) = state.state { + match state { + FrameState::Failed => { + tracing::error!("Frame copy failed"); + return Err(Error::FramecopyFailed); + } + FrameState::Finished => { + tracing::trace!("Frame copy finished"); + + return Ok(DMAFrameGuard { + buffer: dmabuf_wlbuf, + }); + } + } + } + + event_queue.blocking_dispatch(&mut state)?; + } + } + None => todo!(), + } + } + fn capture_output_frame_inner( &self, mut state: CaptureFrameState, @@ -374,23 +572,47 @@ impl WayshotConnection { } } - fn capture_output_frame_shm_from_file( - &self, - cursor_overlay: bool, - output: &WlOutput, - file: &File, - capture_region: Option, - ) -> Result<(FrameFormat, FrameGuard)> { - let (state, event_queue, frame, frame_format) = - self.capture_output_frame_get_state(cursor_overlay as i32, output, capture_region)?; - - file.set_len(frame_format.byte_size())?; - - let frame_guard = - self.capture_output_frame_inner(state, event_queue, frame, frame_format, file)?; - - Ok((frame_format, frame_guard)) - } + // /// Get a FrameCopy instance with screenshot pixel data for any wl_output object via dmabuf. + // #[tracing::instrument(skip_all, fields(output = format!("{output_info}"), region = capture_region.map(|r| format!("{:}", r)).unwrap_or("fullscreen".to_string())))] + // fn capture_frame_copy_dmabuf( + // &self, + // cursor_overlay: bool, + // output_info: &OutputInfo, + // capture_region: Option, + // ) -> Result<(FrameCopy, FrameGuard)> { + // match &self.dmabuf_state { + // Some(dmabuf_state) => { + // let (frame_format, frame_guard, bo) = self.capture_output_frame_dmabuf_from_file( + // cursor_overlay, + // &output_info.wl_output, + // capture_region, + // )?; + // let rotated_physical_size = match output_info.transform { + // Transform::_90 + // | Transform::_270 + // | Transform::Flipped90 + // | Transform::Flipped270 => Size { + // width: frame_format.size.height, + // height: frame_format.size.width, + // }, + // _ => frame_format.size, + // }; + // let frame_copy = FrameCopy { + // frame_format, + // frame_color_type, + // frame_data: FrameData::GBMBo(bo), + // transform: output_info.transform, + // logical_region: capture_region + // .map(|capture_region| capture_region.logical()) + // .unwrap_or(output_info.logical_region), + // physical_size: rotated_physical_size, + // }; + // tracing::debug!("Created frame copy: {:#?}", frame_copy); + // Ok((frame_copy, frame_guard)) + // } + // None => todo!(), + // } + // } /// Get a FrameCopy instance with screenshot pixel data for any wl_output object. #[tracing::instrument(skip_all, fields(output = format!("{output_info}"), region = capture_region.map(|r| format!("{:}", r)).unwrap_or("fullscreen".to_string())))] @@ -433,7 +655,7 @@ impl WayshotConnection { let frame_copy = FrameCopy { frame_format, frame_color_type, - frame_mmap, + frame_data: FrameData::Mmap(frame_mmap), transform: output_info.transform, logical_region: capture_region .map(|capture_region| capture_region.logical()) diff --git a/libwayshot/src/screencopy.rs b/libwayshot/src/screencopy.rs index 8b01c7c6..1bebcd33 100644 --- a/libwayshot/src/screencopy.rs +++ b/libwayshot/src/screencopy.rs @@ -4,6 +4,7 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; +use gbm::BufferObject; use image::{ColorType, DynamicImage, ImageBuffer, Pixel}; use memmap2::MmapMut; use nix::{ @@ -32,6 +33,15 @@ impl Drop for FrameGuard { } } +pub struct DMAFrameGuard { + pub buffer: WlBuffer, +} +impl Drop for DMAFrameGuard { + fn drop(&mut self) { + self.buffer.destroy(); + } +} + /// Type of frame supported by the compositor. For now we only support Argb8888, Xrgb8888, and /// Xbgr8888. /// @@ -46,6 +56,17 @@ pub struct FrameFormat { pub stride: u32, } +/// Type of DMABUF frame supported by the compositor +/// +/// See `zwlr_screencopy_frame_v1::Event::linux_dmabuf` as it's retrieved from there. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct DMAFrameFormat { + pub format: u32, + /// Size of the frame in pixels. This will always be in "landscape" so a + /// portrait 1080x1920 frame will be 1920x1080 and will need to be rotated! + pub size: Size, +} + impl FrameFormat { /// Returns the size of the frame in bytes, which is the stride * height. pub fn byte_size(&self) -> u64 { @@ -53,30 +74,38 @@ impl FrameFormat { } } -#[tracing::instrument(skip(frame_mmap))] +#[tracing::instrument(skip(frame_data))] fn create_image_buffer

( frame_format: &FrameFormat, - frame_mmap: &MmapMut, + frame_data: &FrameData, ) -> Result>> where P: Pixel, { tracing::debug!("Creating image buffer"); - ImageBuffer::from_vec( - frame_format.size.width, - frame_format.size.height, - frame_mmap.to_vec(), - ) - .ok_or(Error::BufferTooSmall) + match frame_data { + FrameData::Mmap(frame_mmap) => ImageBuffer::from_vec( + frame_format.size.width, + frame_format.size.height, + frame_mmap.to_vec(), + ) + .ok_or(Error::BufferTooSmall), + FrameData::GBMBo(_) => todo!(), + } } +#[derive(Debug)] +pub enum FrameData { + Mmap(MmapMut), + GBMBo(BufferObject<()>), +} /// The copied frame comprising of the FrameFormat, ColorType (Rgba8), and a memory backed shm /// file that holds the image data in it. #[derive(Debug)] pub struct FrameCopy { pub frame_format: FrameFormat, pub frame_color_type: ColorType, - pub frame_mmap: MmapMut, + pub frame_data: FrameData, pub transform: wl_output::Transform, /// Logical region with the transform already applied. pub logical_region: LogicalRegion, @@ -89,10 +118,10 @@ impl TryFrom<&FrameCopy> for DynamicImage { fn try_from(value: &FrameCopy) -> Result { Ok(match value.frame_color_type { ColorType::Rgb8 => { - Self::ImageRgb8(create_image_buffer(&value.frame_format, &value.frame_mmap)?) + Self::ImageRgb8(create_image_buffer(&value.frame_format, &value.frame_data)?) } ColorType::Rgba8 => { - Self::ImageRgba8(create_image_buffer(&value.frame_format, &value.frame_mmap)?) + Self::ImageRgba8(create_image_buffer(&value.frame_format, &value.frame_data)?) } _ => return Err(Error::InvalidColor), }) From e64d2e4acc82052b90d4a1932c3fdf6c18734ea8 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Mon, 24 Jun 2024 20:21:17 +0530 Subject: [PATCH 04/21] [refactor] - Add error handling - Add example/demo for wayshot dmabuf API --- libwayshot/examples/waymirror.rs | 204 +++++++++++++++++++++++++++++++ libwayshot/src/error.rs | 10 ++ libwayshot/src/lib.rs | 68 ++--------- 3 files changed, 227 insertions(+), 55 deletions(-) create mode 100644 libwayshot/examples/waymirror.rs diff --git a/libwayshot/examples/waymirror.rs b/libwayshot/examples/waymirror.rs new file mode 100644 index 00000000..c89d7cb6 --- /dev/null +++ b/libwayshot/examples/waymirror.rs @@ -0,0 +1,204 @@ +use libwayshot::WayshotConnection; +use wayland_client::{ + delegate_noop, + protocol::{ + wl_buffer::{self}, + wl_compositor, wl_keyboard, wl_registry, wl_seat, wl_shm, wl_shm_pool, wl_surface, + }, + Connection, Dispatch, QueueHandle, WEnum, +}; + +use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base}; + +fn main() { + let conn = Connection::connect_to_env().unwrap(); + + let mut event_queue = conn.new_event_queue(); + let qhandle = event_queue.handle(); + + let display = conn.display(); + display.get_registry(&qhandle, ()); + let wayshot = WayshotConnection::from_connection_with_dmabuf(conn).unwrap(); + + let mut state = State { + wayshot, + running: true, + base_surface: None, + wm_base: None, + xdg_surface: None, + configured: false, + }; + + println!("Starting the example wayshot dmabuf demo app, press to quit."); + + while state.running { + event_queue.blocking_dispatch(&mut state).unwrap(); + } +} + +struct State { + wayshot: WayshotConnection, + running: bool, + base_surface: Option, + wm_base: Option, + xdg_surface: Option<(xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel)>, + configured: bool, +} + +impl Dispatch for State { + fn event( + state: &mut Self, + registry: &wl_registry::WlRegistry, + event: wl_registry::Event, + _: &(), + _: &Connection, + qh: &QueueHandle, + ) { + if let wl_registry::Event::Global { + name, interface, .. + } = event + { + match &interface[..] { + "wl_compositor" => { + let compositor = + registry.bind::(name, 1, qh, ()); + let surface = compositor.create_surface(qh, ()); + state.base_surface = Some(surface); + + if state.wm_base.is_some() && state.xdg_surface.is_none() { + state.init_xdg_surface(qh); + } + } + "wl_seat" => { + registry.bind::(name, 1, qh, ()); + } + "xdg_wm_base" => { + let wm_base = registry.bind::(name, 1, qh, ()); + state.wm_base = Some(wm_base); + + if state.base_surface.is_some() && state.xdg_surface.is_none() { + state.init_xdg_surface(qh); + } + } + _ => {} + } + } + } +} + +// Ignore events from these object types in this example. +delegate_noop!(State: ignore wl_compositor::WlCompositor); +delegate_noop!(State: ignore wl_surface::WlSurface); +delegate_noop!(State: ignore wl_shm::WlShm); +delegate_noop!(State: ignore wl_shm_pool::WlShmPool); +delegate_noop!(State: ignore wl_buffer::WlBuffer); + +impl State { + fn init_xdg_surface(&mut self, qh: &QueueHandle) { + let wm_base = self.wm_base.as_ref().unwrap(); + let base_surface = self.base_surface.as_ref().unwrap(); + + let xdg_surface = wm_base.get_xdg_surface(base_surface, qh, ()); + let toplevel = xdg_surface.get_toplevel(qh, ()); + toplevel.set_title("DMABuf+wlr-screencpy example!".into()); + + base_surface.commit(); + + self.xdg_surface = Some((xdg_surface, toplevel)); + } +} + +impl Dispatch for State { + fn event( + _: &mut Self, + wm_base: &xdg_wm_base::XdgWmBase, + event: xdg_wm_base::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + if let xdg_wm_base::Event::Ping { serial } = event { + wm_base.pong(serial); + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + xdg_surface: &xdg_surface::XdgSurface, + event: xdg_surface::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + if let xdg_surface::Event::Configure { serial, .. } = event { + xdg_surface.ack_configure(serial); + state.configured = true; + let surface = state.base_surface.as_ref().unwrap(); + let (_frame_format, guard, _bo) = state + .wayshot + .capture_output_frame_dmabuf( + true, + &state.wayshot.get_all_outputs()[0].wl_output, + None, + ) + .unwrap(); + surface.attach(Some(&guard.buffer), 0, 0); + surface.commit(); + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + _: &xdg_toplevel::XdgToplevel, + event: xdg_toplevel::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + if let xdg_toplevel::Event::Close {} = event { + state.running = false; + } + } +} + +impl Dispatch for State { + fn event( + _: &mut Self, + seat: &wl_seat::WlSeat, + event: wl_seat::Event, + _: &(), + _: &Connection, + qh: &QueueHandle, + ) { + if let wl_seat::Event::Capabilities { + capabilities: WEnum::Value(capabilities), + } = event + { + if capabilities.contains(wl_seat::Capability::Keyboard) { + seat.get_keyboard(qh, ()); + } + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + _: &wl_keyboard::WlKeyboard, + event: wl_keyboard::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + if let wl_keyboard::Event::Key { key, .. } = event { + if key == 1 { + // ESC key + state.running = false; + } + } + } +} diff --git a/libwayshot/src/error.rs b/libwayshot/src/error.rs index f3ad8523..0fa36976 100644 --- a/libwayshot/src/error.rs +++ b/libwayshot/src/error.rs @@ -1,5 +1,7 @@ use std::{io, result}; +use drm::buffer::UnrecognizedFourcc; +use gbm::{DeviceDestroyedError, FdError}; use thiserror::Error; use wayland_client::{ globals::{BindError, GlobalError}, @@ -34,4 +36,12 @@ pub enum Error { ProtocolNotFound(String), #[error("error occurred in freeze callback")] FreezeCallbackError, + #[error("dmabuf configuration not initialized")] + NoDMAStateError, + #[error("dmabuf color format provided by compositor is invalid")] + UnrecognizedColorCode(#[from] UnrecognizedFourcc), + #[error("dmabuf device is destroyed")] + DRMDeviceLost(#[from] DeviceDestroyedError), + #[error("obtaining gbm buffer object file descriptor failed")] + GBMBoFdError(#[from] FdError), } diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index 13463a13..2797fb6b 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -247,6 +247,8 @@ impl WayshotConnection { Ok((frame_format, frame_guard)) } + /// 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 pub fn capture_output_frame_dmabuf( &self, cursor_overlay: bool, @@ -262,16 +264,14 @@ impl WayshotConnection { capture_region, )?; let gbm = &dmabuf_state.gbmdev; - let bo = gbm - .create_buffer_object::<()>( - frame_format.size.width, - frame_format.size.height, - gbm::Format::try_from(frame_format.format).unwrap(), - BufferObjectFlags::RENDERING | BufferObjectFlags::LINEAR, - ) - .unwrap(); - let stride = bo.stride().unwrap(); - let modifier: u64 = bo.modifier().unwrap().into(); + let bo = gbm.create_buffer_object::<()>( + frame_format.size.width, + frame_format.size.height, + gbm::Format::try_from(frame_format.format)?, + BufferObjectFlags::RENDERING | BufferObjectFlags::LINEAR, + )?; + let stride = bo.stride()?; + let modifier: u64 = bo.modifier()?.into(); let frame_guard = self.capture_output_frame_inner_dmabuf( state, @@ -280,12 +280,12 @@ impl WayshotConnection { frame_format, stride, modifier, - bo.fd().unwrap(), + bo.fd()?, )?; Ok((frame_format, frame_guard, bo)) } - None => todo!(), + None => Err(Error::NoDMAStateError), } } @@ -514,7 +514,7 @@ impl WayshotConnection { event_queue.blocking_dispatch(&mut state)?; } } - None => todo!(), + None => Err(Error::NoDMAStateError), } } @@ -572,48 +572,6 @@ impl WayshotConnection { } } - // /// Get a FrameCopy instance with screenshot pixel data for any wl_output object via dmabuf. - // #[tracing::instrument(skip_all, fields(output = format!("{output_info}"), region = capture_region.map(|r| format!("{:}", r)).unwrap_or("fullscreen".to_string())))] - // fn capture_frame_copy_dmabuf( - // &self, - // cursor_overlay: bool, - // output_info: &OutputInfo, - // capture_region: Option, - // ) -> Result<(FrameCopy, FrameGuard)> { - // match &self.dmabuf_state { - // Some(dmabuf_state) => { - // let (frame_format, frame_guard, bo) = self.capture_output_frame_dmabuf_from_file( - // cursor_overlay, - // &output_info.wl_output, - // capture_region, - // )?; - // let rotated_physical_size = match output_info.transform { - // Transform::_90 - // | Transform::_270 - // | Transform::Flipped90 - // | Transform::Flipped270 => Size { - // width: frame_format.size.height, - // height: frame_format.size.width, - // }, - // _ => frame_format.size, - // }; - // let frame_copy = FrameCopy { - // frame_format, - // frame_color_type, - // frame_data: FrameData::GBMBo(bo), - // transform: output_info.transform, - // logical_region: capture_region - // .map(|capture_region| capture_region.logical()) - // .unwrap_or(output_info.logical_region), - // physical_size: rotated_physical_size, - // }; - // tracing::debug!("Created frame copy: {:#?}", frame_copy); - // Ok((frame_copy, frame_guard)) - // } - // None => todo!(), - // } - // } - /// Get a FrameCopy instance with screenshot pixel data for any wl_output object. #[tracing::instrument(skip_all, fields(output = format!("{output_info}"), region = capture_region.map(|r| format!("{:}", r)).unwrap_or("fullscreen".to_string())))] fn capture_frame_copy( From eff0053938f01db196813b8b4e45edf58126622f Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Wed, 17 Jul 2024 15:43:00 +0530 Subject: [PATCH 05/21] [feat] import wayland-egl-ctx for use as MVP for dmabuf import functionality --- Cargo.lock | 79 +++ Cargo.toml | 2 +- libwayshot/examples/waymirror-egl/.gitignore | 1 + libwayshot/examples/waymirror-egl/Cargo.lock | 517 ++++++++++++++++++ libwayshot/examples/waymirror-egl/Cargo.toml | 17 + libwayshot/examples/waymirror-egl/README.md | 7 + .../examples/waymirror-egl/src/dispatch.rs | 131 +++++ .../examples/waymirror-egl/src/error.rs | 22 + libwayshot/examples/waymirror-egl/src/main.rs | 66 +++ .../waymirror-egl/src/shaders/frag.glsl | 8 + .../waymirror-egl/src/shaders/vert.glsl | 7 + .../examples/waymirror-egl/src/state.rs | 332 +++++++++++ .../examples/waymirror-egl/src/utils.rs | 31 ++ 13 files changed, 1219 insertions(+), 1 deletion(-) create mode 100644 libwayshot/examples/waymirror-egl/.gitignore create mode 100644 libwayshot/examples/waymirror-egl/Cargo.lock create mode 100644 libwayshot/examples/waymirror-egl/Cargo.toml create mode 100644 libwayshot/examples/waymirror-egl/README.md create mode 100644 libwayshot/examples/waymirror-egl/src/dispatch.rs create mode 100644 libwayshot/examples/waymirror-egl/src/error.rs create mode 100644 libwayshot/examples/waymirror-egl/src/main.rs create mode 100644 libwayshot/examples/waymirror-egl/src/shaders/frag.glsl create mode 100644 libwayshot/examples/waymirror-egl/src/shaders/vert.glsl create mode 100644 libwayshot/examples/waymirror-egl/src/state.rs create mode 100644 libwayshot/examples/waymirror-egl/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 2ac859fc..cbc3c475 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,6 +418,36 @@ dependencies = [ "libc", ] +[[package]] +name = "gl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "gl_loader" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32d96dd5f881490e537041d5532320812ba096097f07fccb4626578da0b99d3" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "glob" version = "0.3.1" @@ -530,6 +560,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + [[package]] name = "lazy_static" version = "1.4.0" @@ -1061,6 +1107,16 @@ dependencies = [ "wayland-scanner", ] +[[package]] +name = "wayland-egl" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355f652e5a24ae02d2ad536c8fc2d3dcc6c2bd635027cd6103a193e7d75eeda2" +dependencies = [ + "wayland-backend", + "wayland-sys", +] + [[package]] name = "wayland-protocols" version = "0.31.2" @@ -1124,6 +1180,23 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "waymirror-egl" +version = "0.1.0" +dependencies = [ + "gl", + "gl_loader", + "khronos-egl", + "libwayshot", + "thiserror", + "tracing", + "tracing-subscriber", + "wayland-backend", + "wayland-client", + "wayland-egl", + "wayland-protocols", +] + [[package]] name = "wayshot" version = "1.3.2-dev" @@ -1267,6 +1340,12 @@ dependencies = [ "wayland-protocols-wlr", ] +[[package]] +name = "xml-rs" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" + [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 4979f4eb..b13e9351 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["wayshot", "libwayshot"] +members = ["wayshot", "libwayshot","libwayshot/examples/waymirror-egl"] [workspace.package] authors = ["Shinyzenith "] diff --git a/libwayshot/examples/waymirror-egl/.gitignore b/libwayshot/examples/waymirror-egl/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/libwayshot/examples/waymirror-egl/.gitignore @@ -0,0 +1 @@ +/target diff --git a/libwayshot/examples/waymirror-egl/Cargo.lock b/libwayshot/examples/waymirror-egl/Cargo.lock new file mode 100644 index 00000000..310b1996 --- /dev/null +++ b/libwayshot/examples/waymirror-egl/Cargo.lock @@ -0,0 +1,517 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "gl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "gl_loader" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32d96dd5f881490e537041d5532320812ba096097f07fccb4626578da0b99d3" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "wayland-backend" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" +dependencies = [ + "bitflags", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-egl" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355f652e5a24ae02d2ad536c8fc2d3dcc6c2bd635027cd6103a193e7d75eeda2" +dependencies = [ + "wayland-backend", + "wayland-sys", +] + +[[package]] +name = "wayland-egl-ctx" +version = "0.1.0" +dependencies = [ + "gl", + "gl_loader", + "khronos-egl", + "thiserror", + "tracing", + "tracing-subscriber", + "wayland-backend", + "wayland-client", + "wayland-egl", + "wayland-protocols", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" diff --git a/libwayshot/examples/waymirror-egl/Cargo.toml b/libwayshot/examples/waymirror-egl/Cargo.toml new file mode 100644 index 00000000..9dd6fd39 --- /dev/null +++ b/libwayshot/examples/waymirror-egl/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "waymirror-egl" +version = "0.1.0" +edition = "2021" + +[dependencies] +gl = "0.14.0" +gl_loader = "0.1.2" +khronos-egl = { version = "6.0.0", features = ["static"] } +thiserror = "1.0.58" +tracing = "0.1.40" +tracing-subscriber = "0.3.18" +wayland-backend = { version = "0.3.3", features = ["client_system"] } +wayland-client = { version = "0.31.2" } +wayland-egl = { version = "0.32.0" } +wayland-protocols = { version = "0.31.2", features = ["client"] } +libwayshot={path="../.."} \ No newline at end of file diff --git a/libwayshot/examples/waymirror-egl/README.md b/libwayshot/examples/waymirror-egl/README.md new file mode 100644 index 00000000..ad3b0d01 --- /dev/null +++ b/libwayshot/examples/waymirror-egl/README.md @@ -0,0 +1,7 @@ +# wayland-egl-ctx + +Example wayland xdg window driven using egl + +# TODO + +- [x] Get GL calls to work diff --git a/libwayshot/examples/waymirror-egl/src/dispatch.rs b/libwayshot/examples/waymirror-egl/src/dispatch.rs new file mode 100644 index 00000000..2e3f38f8 --- /dev/null +++ b/libwayshot/examples/waymirror-egl/src/dispatch.rs @@ -0,0 +1,131 @@ +use crate::state::WaylandEGLState; +use wayland_client::{ + delegate_noop, + protocol::{wl_compositor, wl_registry, wl_surface}, + Connection, Dispatch, QueueHandle, +}; +use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base}; + +impl Dispatch for WaylandEGLState { + #[tracing::instrument(skip(registry, queue_handle, state), ret, level = "trace")] + fn event( + state: &mut Self, + registry: &wl_registry::WlRegistry, + event: wl_registry::Event, + _: &(), + _: &Connection, + queue_handle: &QueueHandle, + ) { + if let wl_registry::Event::Global { + name, + interface, + version, + } = event + { + match interface.as_str() { + "xdg_wm_base" => { + state.xdg_wm_base = Some(registry.bind::( + name, + version, + queue_handle, + (), + )); + } + "wl_compositor" => { + state.wl_compositor = Some(registry.bind::( + name, + version, + queue_handle, + (), + )); + } + _ => {} + } + } + } +} + +impl Dispatch for WaylandEGLState { + #[tracing::instrument(skip(xdg_wm_base), ret, level = "trace")] + fn event( + _: &mut Self, + xdg_wm_base: &xdg_wm_base::XdgWmBase, + event: xdg_wm_base::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + if let xdg_wm_base::Event::Ping { serial } = event { + xdg_wm_base.pong(serial); + } + } +} + +impl Dispatch for WaylandEGLState { + #[tracing::instrument(skip(xdg_surface), ret, level = "trace")] + fn event( + _: &mut Self, + xdg_surface: &xdg_surface::XdgSurface, + event: xdg_surface::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + if let xdg_surface::Event::Configure { serial } = event { + xdg_surface.ack_configure(serial); + } + } +} + +impl Dispatch for WaylandEGLState { + #[tracing::instrument(skip(), ret, level = "trace")] + fn event( + state: &mut Self, + _: &xdg_toplevel::XdgToplevel, + event: xdg_toplevel::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + match event { + xdg_toplevel::Event::Configure { width, height, .. } => { + if width == 0 || height == 0 { + return; // We do not respect this configure + } + + if state.width != width || state.height != height { + state.width = width; + state.height = height; + + state + .egl_window + .clone() + .unwrap() + .resize(state.width, state.height, 0, 0); + + unsafe { + gl::Viewport(0, 0, state.width, state.height); + } + state.wl_surface.clone().unwrap().commit(); + } + } + xdg_toplevel::Event::Close {} => { + state.running = false; + } + _ => {} + } + } +} + +impl Dispatch for WaylandEGLState { + fn event( + _state: &mut Self, + _proxy: &wl_surface::WlSurface, + _event: ::Event, + _data: &(), + _conn: &Connection, + _qhandle: &QueueHandle, + ) { + } +} +delegate_noop!(WaylandEGLState: wl_compositor::WlCompositor); diff --git a/libwayshot/examples/waymirror-egl/src/error.rs b/libwayshot/examples/waymirror-egl/src/error.rs new file mode 100644 index 00000000..1860cc85 --- /dev/null +++ b/libwayshot/examples/waymirror-egl/src/error.rs @@ -0,0 +1,22 @@ +use std::result; +use thiserror::Error; + +pub type Result = result::Result; + +#[derive(Error, Debug)] +pub enum WaylandEGLStateError { + #[error("xdg_wm_base global missing")] + XdgWmBaseMissing, + + #[error("wl_compositor global missing")] + WlCompositorMissing, + + #[error("Shader compilation failed")] + GLShaderCompileFailed, + + #[error("Failed to create gl program")] + GLCreateProgramFailed, + + #[error("Failed to link gl program")] + GLLinkProgramFailed, +} diff --git a/libwayshot/examples/waymirror-egl/src/main.rs b/libwayshot/examples/waymirror-egl/src/main.rs new file mode 100644 index 00000000..34aed15b --- /dev/null +++ b/libwayshot/examples/waymirror-egl/src/main.rs @@ -0,0 +1,66 @@ +mod dispatch; +mod error; +mod state; +mod utils; + +use error::Result; +use state::WaylandEGLState; +use std::str::FromStr; + +pub fn main() -> Result<(), Box> { + tracing_subscriber::fmt() + .with_max_level(tracing::Level::from_str("trace")?) + .with_writer(std::io::stderr) + .init(); + + let mut state = WaylandEGLState::new()?; + let mut event_queue = state.wl_connection.new_event_queue(); + + let queue_handle = event_queue.handle(); + let _registry = state.wl_display.get_registry(&queue_handle, ()); + + event_queue.roundtrip(&mut state)?; + state.validate_globals()?; + + state.wl_surface = Some( + state + .wl_compositor + .as_ref() + .unwrap() + .create_surface(&queue_handle, ()), + ); + + state.xdg_surface = Some(state.xdg_wm_base.clone().unwrap().get_xdg_surface( + &state.wl_surface.clone().unwrap(), + &queue_handle, + (), + )); + state.xdg_toplevel = Some( + state + .xdg_surface + .clone() + .unwrap() + .get_toplevel(&queue_handle, ()), + ); + state + .xdg_toplevel + .clone() + .unwrap() + .set_title(state.title.clone()); + state.wl_surface.clone().unwrap().commit(); + + state.init_egl()?; + while state.running { + event_queue.dispatch_pending(&mut state)?; + + state.draw(); + state + .egl + .swap_buffers(state.egl_display.unwrap(), state.egl_surface.unwrap())?; + + tracing::event!(tracing::Level::DEBUG, "eglSwapBuffers called"); + } + state.deinit()?; + + Ok(()) +} diff --git a/libwayshot/examples/waymirror-egl/src/shaders/frag.glsl b/libwayshot/examples/waymirror-egl/src/shaders/frag.glsl new file mode 100644 index 00000000..165f300a --- /dev/null +++ b/libwayshot/examples/waymirror-egl/src/shaders/frag.glsl @@ -0,0 +1,8 @@ +#version 300 es + +precision mediump float; +out vec4 FragColor; + +void main() { + FragColor = vec4 ( 1.0, 0.2, 0.0, 1.0 ); +} diff --git a/libwayshot/examples/waymirror-egl/src/shaders/vert.glsl b/libwayshot/examples/waymirror-egl/src/shaders/vert.glsl new file mode 100644 index 00000000..7523d9c2 --- /dev/null +++ b/libwayshot/examples/waymirror-egl/src/shaders/vert.glsl @@ -0,0 +1,7 @@ +#version 300 es + +layout(location = 0) in vec3 aPos; + +void main() { + gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); +} diff --git a/libwayshot/examples/waymirror-egl/src/state.rs b/libwayshot/examples/waymirror-egl/src/state.rs new file mode 100644 index 00000000..505e5741 --- /dev/null +++ b/libwayshot/examples/waymirror-egl/src/state.rs @@ -0,0 +1,332 @@ +use crate::error::{Result, WaylandEGLStateError}; +use crate::utils::load_shader; + +use libwayshot::WayshotConnection; + +use gl::types::GLuint; +use khronos_egl::{self as egl, Attrib}; +use std::os::fd::AsRawFd; +use std::{ffi::c_void, rc::Rc}; +use wayland_client::{ + protocol::{wl_compositor, wl_display::WlDisplay, wl_surface::WlSurface}, + ConnectError, Connection, Proxy, +}; +use wayland_egl::WlEglSurface; +use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base}; + +#[derive(Debug)] +pub struct WaylandEGLState { + pub width: i32, + pub height: i32, + pub running: bool, + pub title: String, + + pub wl_connection: Connection, + pub wl_display: WlDisplay, + pub wl_surface: Option, + + pub egl: egl::Instance, + pub egl_window: Option>, + pub egl_display: Option, + pub egl_surface: Option, + pub egl_context: Option, + pub egl_image: Option, + + pub gl_program: GLuint, + + pub xdg_wm_base: Option, + pub xdg_surface: Option, + pub xdg_toplevel: Option, + pub wl_compositor: Option, + + wayshot: WayshotConnection, +} + +impl WaylandEGLState { + #[tracing::instrument] + pub fn new() -> Result { + let server_connection = Connection::connect_to_env()?; + Ok(Self { + width: 320, + height: 240, + running: true, + title: "Nya".into(), + + wl_connection: server_connection.clone(), + wl_display: server_connection.display(), + wl_surface: None, + + egl: egl::Instance::new(egl::Static), + egl_window: None, + egl_display: None, + egl_surface: None, + egl_context: None, + egl_image: None, + gl_program: 0, + + xdg_wm_base: None, + xdg_surface: None, + xdg_toplevel: None, + wl_compositor: None, + wayshot: WayshotConnection::from_connection_with_dmabuf(server_connection).unwrap(), + }) + } + + pub fn deinit(&self) -> Result<(), Box> { + unsafe { + gl::DeleteProgram(self.gl_program); + } + + self.egl + .destroy_surface(self.egl_display.unwrap(), self.egl_surface.unwrap())?; + self.egl + .destroy_context(self.egl_display.unwrap(), self.egl_context.unwrap())?; + + self.xdg_surface.clone().unwrap().destroy(); + self.wl_surface.clone().unwrap().destroy(); + + Ok(()) + } + + pub fn init_egl(&mut self) -> Result<(), Box> { + // Init gl + gl_loader::init_gl(); + gl::load_with(|s| gl_loader::get_proc_address(s) as *const _); + + self.egl_window = Some(Rc::new(WlEglSurface::new( + self.wl_surface.clone().unwrap().id(), + self.width, + self.height, + )?)); + + self.egl_display = Some( + unsafe { + self.egl + .get_display(self.wl_display.id().as_ptr() as *mut c_void) + } + .unwrap(), + ); + + self.egl.initialize(self.egl_display.unwrap())?; + + let attributes = [ + egl::SURFACE_TYPE, + egl::WINDOW_BIT, + egl::RENDERABLE_TYPE, + egl::OPENGL_ES2_BIT, + egl::RED_SIZE, + 8, + egl::GREEN_SIZE, + 8, + egl::BLUE_SIZE, + 8, + egl::ALPHA_SIZE, + 8, + egl::NONE, + ]; + + let config = self + .egl + .choose_first_config(self.egl_display.unwrap(), &attributes)? + .expect("unable to find an appropriate EGL configuration"); + + self.egl_surface = Some(unsafe { + self.egl.create_window_surface( + self.egl_display.unwrap(), + config, + self.egl_window.clone().unwrap().ptr() as egl::NativeWindowType, + None, + )? + }); + + let context_attributes = [egl::CONTEXT_CLIENT_VERSION, 2, egl::NONE, egl::NONE]; + self.egl_context = Some(self.egl.create_context( + self.egl_display.unwrap(), + config, + None, + &context_attributes, + )?); + + self.egl.make_current( + self.egl_display.unwrap(), + self.egl_surface, + self.egl_surface, + self.egl_context, + )?; + + self.init_program()?; + let (frame_format, _guard, bo) = self + .wayshot + .capture_output_frame_dmabuf(true, &self.wayshot.get_all_outputs()[0].wl_output, None) + .unwrap(); + let modifier: u64 = dbg!(bo.modifier().unwrap().into()); + 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 + dbg!(bo.fd_for_plane(0).unwrap().as_raw_fd()) as Attrib, + 0x3273, + 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 & u32::MAX) as Attrib, + 0x3444, //EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT + (modifier >> 32) as Attrib, + egl::ATTRIB_NONE, + ]; + dbg!(image_attribs, image_attribs.len()); + unsafe { + let egl_create_image_khr: 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("eglCreateImageKHR") + .unwrap())); + let image = egl_create_image_khr( + self.egl_display.unwrap().as_ptr(), + egl::NO_CONTEXT as *mut c_void, + 0x3270, + std::ptr::null_mut(), + image_attribs.as_ptr(), + ); + dbg!(image, self.egl.get_error().unwrap()); + assert_ne!(image, 0 as *mut c_void); + } + // 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() + // }); + + Ok(()) + } + + fn init_program(&mut self) -> Result<()> { + let vert_shader = load_shader( + gl::VERTEX_SHADER, + include_str!("./shaders/vert.glsl").into(), + ) + .unwrap(); + + let frag_shader = load_shader( + gl::FRAGMENT_SHADER, + include_str!("./shaders/frag.glsl").into(), + ) + .unwrap(); + + unsafe { + self.gl_program = gl::CreateProgram(); + } + + if self.gl_program == 0 { + tracing::event!(tracing::Level::ERROR, "glCreateProgramFailed!"); + return Err(WaylandEGLStateError::GLCreateProgramFailed); + } + + unsafe { + gl::AttachShader(self.gl_program, vert_shader); + gl::AttachShader(self.gl_program, frag_shader); + + gl::LinkProgram(self.gl_program); + } + + let mut linked: gl::types::GLint = 1; + unsafe { gl::GetProgramiv(self.gl_program, gl::LINK_STATUS, &mut linked as *mut i32) } + + if linked > 0 { + tracing::event!(tracing::Level::INFO, "Successfully linked the program!"); + } else { + return Err(WaylandEGLStateError::GLLinkProgramFailed); + } + let vertices: [gl::types::GLfloat; 12] = [ + 0.9, 0.9, 0.0, // top right + 0.9, -0.9, 0.0, // bottom right + -0.9, -0.9, 0.0, // bottom left + -0.9, 0.9, 0.0, // top left + ]; + let indices: [gl::types::GLint; 6] = [ + 0, 1, 3, // first Triangle + 1, 2, 3, // second Triangle + ]; + let mut vbo: GLuint = 0; + let mut vao: GLuint = 0; + let mut ebo: GLuint = 0; + unsafe { + 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); + gl::BufferData( + gl::ARRAY_BUFFER, + (vertices.len() * std::mem::size_of::()) + as gl::types::GLsizeiptr, + &vertices[0] as *const f32 as *const c_void, + gl::STATIC_DRAW, + ); + + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo); + gl::BufferData( + gl::ELEMENT_ARRAY_BUFFER, + (indices.len() * std::mem::size_of::()) + as gl::types::GLsizeiptr, + &indices[0] as *const i32 as *const c_void, + gl::STATIC_DRAW, + ); + + gl::VertexAttribPointer( + 0, + 3, + gl::FLOAT, + gl::FALSE, + 3 * std::mem::size_of::() as gl::types::GLint, + 0 as *const c_void, + ); + gl::EnableVertexAttribArray(0); + gl::BindBuffer(gl::ARRAY_BUFFER, 0); + + //gl::BindVertexArray(0); + } + Ok(()) + } + + pub fn draw(&self) { + unsafe { + gl::ClearColor(1.0, 1.0, 0.0, 1.0); + gl::Clear(gl::COLOR_BUFFER_BIT); + + gl::UseProgram(self.gl_program); + + //gl::BindVertexArray(1); + gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, 0 as *const c_void); + dbg!(gl::GetError()); + } + } + + pub fn validate_globals(&self) -> Result<()> { + if self.xdg_wm_base.is_none() { + return Err(WaylandEGLStateError::XdgWmBaseMissing); + } else if self.wl_compositor.is_none() { + return Err(WaylandEGLStateError::WlCompositorMissing); + } + + Ok(()) + } +} diff --git a/libwayshot/examples/waymirror-egl/src/utils.rs b/libwayshot/examples/waymirror-egl/src/utils.rs new file mode 100644 index 00000000..e532e2d3 --- /dev/null +++ b/libwayshot/examples/waymirror-egl/src/utils.rs @@ -0,0 +1,31 @@ +use crate::error::{Result, WaylandEGLStateError}; +use gl::types::{GLenum, GLint, GLuint}; +use std::{ffi::CString, ptr}; + +pub fn load_shader(shader_type: GLenum, src: String) -> Result { + unsafe { + let shader: GLuint = gl::CreateShader(shader_type); + + if shader == 0 { + return Err(WaylandEGLStateError::GLShaderCompileFailed); + } + + let src_c_str = CString::new(src.as_bytes()).unwrap(); + gl::ShaderSource(shader, 1, &src_c_str.as_ptr(), ptr::null()); + + //gl::ShaderSource(shader, 1, &src_c_str.as_ptr(), std::ptr::null()); + + gl::CompileShader(shader); + + let mut status: GLint = 1; + gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status as *mut i32); + + if status > 0 { + tracing::event!(tracing::Level::INFO, "Shader compile successfull!",); + } else { + return Err(WaylandEGLStateError::GLShaderCompileFailed); + } + + Ok(shader) + } +} From e934677d316b4c1a560f715e054227d02953d764 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Wed, 17 Jul 2024 18:07:21 +0530 Subject: [PATCH 06/21] [fix] correct modifier_lo parameter in waymirror-egl MVP --- libwayshot/examples/waymirror-egl/src/state.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libwayshot/examples/waymirror-egl/src/state.rs b/libwayshot/examples/waymirror-egl/src/state.rs index 505e5741..460109b9 100644 --- a/libwayshot/examples/waymirror-egl/src/state.rs +++ b/libwayshot/examples/waymirror-egl/src/state.rs @@ -160,6 +160,7 @@ impl WaylandEGLState { .capture_output_frame_dmabuf(true, &self.wayshot.get_all_outputs()[0].wl_output, None) .unwrap(); 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, @@ -174,7 +175,7 @@ impl WaylandEGLState { 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 & u32::MAX) as Attrib, + (modifier as u32) as Attrib, 0x3444, //EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT (modifier >> 32) as Attrib, egl::ATTRIB_NONE, From 8d410d74580501c6ea05519f929fbf85640e51e7 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Sat, 27 Jul 2024 18:09:23 +0530 Subject: [PATCH 07/21] [feat] get waymirror-egl dmabuf MVP demo working --- Cargo.lock | 7 +- libwayshot/examples/waymirror-egl/Cargo.toml | 5 +- libwayshot/examples/waymirror-egl/src/main.rs | 7 +- .../waymirror-egl/src/shaders/frag.glsl | 5 +- .../waymirror-egl/src/shaders/vert.glsl | 12 +- .../examples/waymirror-egl/src/state.rs | 257 ++++++++++++------ libwayshot/src/lib.rs | 2 +- 7 files changed, 201 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbc3c475..ffd3fda3 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", - "pkg-config", + "libloading", ] [[package]] @@ -590,9 +590,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if", "windows-targets", @@ -1187,6 +1187,7 @@ dependencies = [ "gl", "gl_loader", "khronos-egl", + "libloading", "libwayshot", "thiserror", "tracing", diff --git a/libwayshot/examples/waymirror-egl/Cargo.toml b/libwayshot/examples/waymirror-egl/Cargo.toml index 9dd6fd39..4653f73d 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 = ["static"] } +khronos-egl = { version = "6.0.0",features = ["dynamic"] } thiserror = "1.0.58" tracing = "0.1.40" tracing-subscriber = "0.3.18" @@ -14,4 +14,5 @@ wayland-backend = { version = "0.3.3", features = ["client_system"] } wayland-client = { version = "0.31.2" } wayland-egl = { version = "0.32.0" } wayland-protocols = { version = "0.31.2", features = ["client"] } -libwayshot={path="../.."} \ No newline at end of file +libwayshot={path="../.."} +libloading="0.8.4" \ No newline at end of file diff --git a/libwayshot/examples/waymirror-egl/src/main.rs b/libwayshot/examples/waymirror-egl/src/main.rs index 34aed15b..63e04efb 100644 --- a/libwayshot/examples/waymirror-egl/src/main.rs +++ b/libwayshot/examples/waymirror-egl/src/main.rs @@ -9,7 +9,7 @@ use std::str::FromStr; pub fn main() -> Result<(), Box> { tracing_subscriber::fmt() - .with_max_level(tracing::Level::from_str("trace")?) + .with_max_level(tracing::Level::from_str("error")?) .with_writer(std::io::stderr) .init(); @@ -52,13 +52,14 @@ pub fn main() -> Result<(), Box> { state.init_egl()?; while state.running { event_queue.dispatch_pending(&mut state)?; - + // event_queue.blocking_dispatch(&mut state)?; + // state.dmabuf_to_egl() state.draw(); state .egl .swap_buffers(state.egl_display.unwrap(), state.egl_surface.unwrap())?; - tracing::event!(tracing::Level::DEBUG, "eglSwapBuffers called"); + //tracing::event!(tracing::Level::DEBUG, "eglSwapBuffers called"); } state.deinit()?; diff --git a/libwayshot/examples/waymirror-egl/src/shaders/frag.glsl b/libwayshot/examples/waymirror-egl/src/shaders/frag.glsl index 165f300a..8212a377 100644 --- a/libwayshot/examples/waymirror-egl/src/shaders/frag.glsl +++ b/libwayshot/examples/waymirror-egl/src/shaders/frag.glsl @@ -2,7 +2,10 @@ precision mediump float; out vec4 FragColor; +uniform sampler2D uTexture; +in vec2 vTexCoord; void main() { - FragColor = vec4 ( 1.0, 0.2, 0.0, 1.0 ); + vec4 color = texture(uTexture, vTexCoord); + FragColor = vec4 ( 1.0-color.r,1.0-color.g,1.0-color.b, 1.0 ); } diff --git a/libwayshot/examples/waymirror-egl/src/shaders/vert.glsl b/libwayshot/examples/waymirror-egl/src/shaders/vert.glsl index 7523d9c2..9b27c568 100644 --- a/libwayshot/examples/waymirror-egl/src/shaders/vert.glsl +++ b/libwayshot/examples/waymirror-egl/src/shaders/vert.glsl @@ -1,7 +1,11 @@ #version 300 es +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTexCoord; -layout(location = 0) in vec3 aPos; +out vec2 vTexCoord; -void main() { - gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); -} +void main() +{ + gl_Position = vec4(aPos, 1.0); + vTexCoord = vec2(aTexCoord.x, aTexCoord.y); +} \ No newline at end of file diff --git a/libwayshot/examples/waymirror-egl/src/state.rs b/libwayshot/examples/waymirror-egl/src/state.rs index 460109b9..c73b0790 100644 --- a/libwayshot/examples/waymirror-egl/src/state.rs +++ b/libwayshot/examples/waymirror-egl/src/state.rs @@ -3,9 +3,9 @@ use crate::utils::load_shader; use libwayshot::WayshotConnection; -use gl::types::GLuint; -use khronos_egl::{self as egl, Attrib}; -use std::os::fd::AsRawFd; +use gl::types::{GLeglImageOES, GLuint}; +use khronos_egl::{self as egl, Attrib, DynamicInstance, EGLClientBuffer}; +use std::os::fd::{AsRawFd, IntoRawFd}; use std::{ffi::c_void, rc::Rc}; use wayland_client::{ protocol::{wl_compositor, wl_display::WlDisplay, wl_surface::WlSurface}, @@ -25,7 +25,7 @@ pub struct WaylandEGLState { pub wl_display: WlDisplay, pub wl_surface: Option, - pub egl: egl::Instance, + pub egl: egl::DynamicInstance, pub egl_window: Option>, pub egl_display: Option, pub egl_surface: Option, @@ -33,6 +33,7 @@ pub struct WaylandEGLState { pub egl_image: Option, pub gl_program: GLuint, + pub gl_texture: GLuint, pub xdg_wm_base: Option, pub xdg_surface: Option, @@ -46,9 +47,16 @@ 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: 320, - height: 240, + width: 1920, + height: 1080, running: true, title: "Nya".into(), @@ -56,13 +64,14 @@ impl WaylandEGLState { wl_display: server_connection.display(), wl_surface: None, - egl: egl::Instance::new(egl::Static), + egl: egl, egl_window: None, egl_display: None, egl_surface: None, egl_context: None, egl_image: None, gl_program: 0, + gl_texture: 0, xdg_wm_base: None, xdg_surface: None, @@ -120,8 +129,6 @@ impl WaylandEGLState { 8, egl::BLUE_SIZE, 8, - egl::ALPHA_SIZE, - 8, egl::NONE, ]; @@ -129,7 +136,7 @@ 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(), @@ -139,7 +146,7 @@ impl WaylandEGLState { )? }); - let context_attributes = [egl::CONTEXT_CLIENT_VERSION, 2, egl::NONE, egl::NONE]; + let context_attributes = [egl::CONTEXT_CLIENT_VERSION, 2, egl::NONE]; self.egl_context = Some(self.egl.create_context( self.egl_display.unwrap(), config, @@ -155,64 +162,6 @@ impl WaylandEGLState { )?; self.init_program()?; - let (frame_format, _guard, bo) = self - .wayshot - .capture_output_frame_dmabuf(true, &self.wayshot.get_all_outputs()[0].wl_output, None) - .unwrap(); - 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 - dbg!(bo.fd_for_plane(0).unwrap().as_raw_fd()) as Attrib, - 0x3273, - 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, - ]; - dbg!(image_attribs, image_attribs.len()); - unsafe { - let egl_create_image_khr: 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("eglCreateImageKHR") - .unwrap())); - let image = egl_create_image_khr( - self.egl_display.unwrap().as_ptr(), - egl::NO_CONTEXT as *mut c_void, - 0x3270, - std::ptr::null_mut(), - image_attribs.as_ptr(), - ); - dbg!(image, self.egl.get_error().unwrap()); - assert_ne!(image, 0 as *mut c_void); - } - // 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() - // }); Ok(()) } @@ -254,11 +203,13 @@ impl WaylandEGLState { } else { return Err(WaylandEGLStateError::GLLinkProgramFailed); } - let vertices: [gl::types::GLfloat; 12] = [ - 0.9, 0.9, 0.0, // top right - 0.9, -0.9, 0.0, // bottom right - -0.9, -0.9, 0.0, // bottom left - -0.9, 0.9, 0.0, // top left + + let vertices: [gl::types::GLfloat; 20] = [ + // positions // texture coords + 1.0, 1.0, 0.0, 1.0, 0.0, // top right + 1.0, -1.0, 0.0, 1.0, 1.0, // bottom right + -1.0, -1.0, 0.0, 0.0, 1.0, // bottom left + -1.0, 1.0, 0.0, 0.0, 0.0, // top left ]; let indices: [gl::types::GLint; 6] = [ 0, 1, 3, // first Triangle @@ -267,7 +218,12 @@ impl WaylandEGLState { let mut vbo: GLuint = 0; let mut vao: GLuint = 0; let mut ebo: GLuint = 0; + unsafe { + gl::GenTextures(1, &mut self.gl_texture); + + self.dmabuf_to_egl(); + gl::GenVertexArrays(1, &mut vao as *mut u32); gl::GenBuffers(1, &mut vbo as *mut u32); gl::GenBuffers(1, &mut ebo as *mut u32); @@ -297,30 +253,171 @@ impl WaylandEGLState { 3, gl::FLOAT, gl::FALSE, - 3 * std::mem::size_of::() as gl::types::GLint, + 5 * std::mem::size_of::() as gl::types::GLint, 0 as *const c_void, ); gl::EnableVertexAttribArray(0); - gl::BindBuffer(gl::ARRAY_BUFFER, 0); + gl::VertexAttribPointer( + 1, + 2, + gl::FLOAT, + gl::FALSE, + 5 * std::mem::size_of::() as gl::types::GLint, + (3 * std::mem::size_of::()) as *const c_void, + ); + gl::EnableVertexAttribArray(1); + gl::BindBuffer(gl::ARRAY_BUFFER, 0); //gl::BindVertexArray(0); } Ok(()) } - pub fn draw(&self) { + pub fn draw(&mut self) { unsafe { 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(); gl::UseProgram(self.gl_program); - + //gl::BindTexture(gl::TEXTURE_2D, self.gl_texture); //gl::BindVertexArray(1); gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, 0 as *const c_void); - dbg!(gl::GetError()); } } + pub fn dmabuf_to_egl(&self) { + type Attrib = egl::Attrib; + let (frame_format, guard, bo) = self + .wayshot + .capture_output_frame_dmabuf(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, + ) -> () = std::mem::transmute( + self.egl + .get_proc_address("glEGLImageTargetTexture2DOES") + .unwrap(), + ); + + gl::BindTexture(gl::TEXTURE_2D, self.gl_texture); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); + + gl_egl_image_texture_target_2d_oes(gl::TEXTURE_2D, image.as_ptr()); + 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<()> { if self.xdg_wm_base.is_none() { return Err(WaylandEGLStateError::XdgWmBaseMissing); diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index 2797fb6b..9fc6c86d 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -280,7 +280,7 @@ impl WayshotConnection { frame_format, stride, modifier, - bo.fd()?, + bo.fd_for_plane(0)?, )?; Ok((frame_format, frame_guard, bo)) From 4d2c574cd3f65e1ac8664c29ff717ee4aa215de0 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Tue, 13 Aug 2024 18:23:07 +0530 Subject: [PATCH 08/21] [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 From bf32c4cbabf8e50fee8da076527fc5d8e4e9d2f8 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Fri, 16 Aug 2024 22:17:20 +0530 Subject: [PATCH 09/21] [feat] - Implemented clean dropping of EGLImage - Improved API docs for dmabuf functions --- libwayshot/examples/waymirror-egl/src/main.rs | 1 - .../examples/waymirror-egl/src/state.rs | 24 +++--- libwayshot/src/lib.rs | 78 +++++++++++++++---- libwayshot/src/screencopy.rs | 16 ++++ 4 files changed, 90 insertions(+), 29 deletions(-) diff --git a/libwayshot/examples/waymirror-egl/src/main.rs b/libwayshot/examples/waymirror-egl/src/main.rs index 63e04efb..8af37432 100644 --- a/libwayshot/examples/waymirror-egl/src/main.rs +++ b/libwayshot/examples/waymirror-egl/src/main.rs @@ -53,7 +53,6 @@ pub fn main() -> Result<(), Box> { while state.running { event_queue.dispatch_pending(&mut state)?; // event_queue.blocking_dispatch(&mut state)?; - // state.dmabuf_to_egl() state.draw(); state .egl diff --git a/libwayshot/examples/waymirror-egl/src/state.rs b/libwayshot/examples/waymirror-egl/src/state.rs index 1f7afe3a..2f93c612 100644 --- a/libwayshot/examples/waymirror-egl/src/state.rs +++ b/libwayshot/examples/waymirror-egl/src/state.rs @@ -51,7 +51,7 @@ impl WaylandEGLState { width: 1920, height: 1080, running: true, - title: "Nya".into(), + title: "Waymirror-EGL".into(), wl_connection: server_connection.clone(), wl_display: server_connection.display(), @@ -259,8 +259,8 @@ impl WaylandEGLState { ); gl::EnableVertexAttribArray(1); gl::BindBuffer(gl::ARRAY_BUFFER, 0); - //gl::BindVertexArray(0); } + self.dmabuf_to_texture(); Ok(()) } @@ -268,19 +268,22 @@ impl WaylandEGLState { unsafe { 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_texture(); + // gl::DeleteTextures(1, &mut self.gl_texture); + gl::UseProgram(self.gl_program); - //gl::BindTexture(gl::TEXTURE_2D, self.gl_texture); - //gl::BindVertexArray(1); gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, 0 as *const c_void); } } pub fn dmabuf_to_texture(&self) { - let image = self + let eglimage_guard = self .wayshot - .capture_output_frame_eglimage(true, &self.wayshot.get_all_outputs()[0].wl_output, None) + .capture_output_frame_eglimage( + &self.egl, + true, + &self.wayshot.get_all_outputs()[0].wl_output, + None, + ) .unwrap(); unsafe { let gl_egl_image_texture_target_2d_oes: unsafe extern "system" fn( @@ -296,10 +299,7 @@ impl WaylandEGLState { gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); - gl_egl_image_texture_target_2d_oes(gl::TEXTURE_2D, image.as_ptr()); - self.egl - .destroy_image(self.egl_display.unwrap(), image) - .unwrap(); + gl_egl_image_texture_target_2d_oes(gl::TEXTURE_2D, eglimage_guard.image.as_ptr()); } } diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index 502a3c8a..4f6e9bcd 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -25,7 +25,7 @@ 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}; +use screencopy::{DMAFrameFormat, DMAFrameGuard, EGLImageGuard, FrameData, FrameGuard}; use tracing::debug; use wayland_client::{ globals::{registry_queue_init, GlobalList}, @@ -222,40 +222,72 @@ impl WayshotConnection { Ok((frame_format, frame_guard)) } - pub fn capture_output_frame_eglimage( + + /// Obtain a screencapture in the form of a EGLImage. + /// The display on which this image is created is obtained from the Wayland Connection. + /// Uses the dma-buf provisions of the wlr-screencopy copy protocol to avoid VRAM->RAM copies + /// It returns the captured frame as an `EGLImage`, wrapped in an `EGLImageGuard` + /// for safe handling and cleanup. + /// # Parameters + /// - `egl_instance`: Reference to an `EGL1_5` instance, which is used to create the `EGLImage`. + /// - `cursor_overlay`: A boolean flag indicating whether the cursor should be included in the capture. + /// - `output`: Reference to the `WlOutput` from which the frame is to be captured. + /// - `capture_region`: Optional region specifying a sub-area of the output to capture. If `None`, the entire output is captured. + /// + /// # Returns + /// If successful, an EGLImageGuard which contains a pointer 'image' to the created EGLImage + /// On error, the EGL [error code](https://registry.khronos.org/EGL/sdk/docs/man/html/eglGetError.xhtml) is returned via this crates Error type + pub fn capture_output_frame_eglimage<'a, T: khronos_egl::api::EGL1_5>( &self, + egl_instance: &'a Instance, cursor_overlay: bool, output: &WlOutput, capture_region: Option, - ) -> Result { - let egl = khronos_egl::Instance::new(egl::Static); + ) -> Result> { let egl_display = unsafe { - match egl.get_display(self.conn.display().id().as_ptr() as *mut c_void) { + match egl_instance.get_display(self.conn.display().id().as_ptr() as *mut c_void) { Some(disp) => disp, - None => return Err(egl.get_error().unwrap().into()), + None => return Err(egl_instance.get_error().unwrap().into()), } }; - egl.initialize(egl_display)?; + egl_instance.initialize(egl_display)?; self.capture_output_frame_eglimage_on_display( - &egl, + &egl_instance, egl_display, cursor_overlay, output, capture_region, ) } - pub fn capture_output_frame_eglimage_on_display( + + /// Obtain a screencapture in the form of a EGLImage on the given EGLDisplay. + /// + /// Uses the dma-buf provisions of the wlr-screencopy copy protocol to avoid VRAM->RAM copies + /// It returns the captured frame as an `EGLImage`, wrapped in an `EGLImageGuard` + /// for safe handling and cleanup. + /// # Parameters + /// - `egl_instance`: Reference to an `EGL1_5` instance, which is used to create the `EGLImage`. + /// - `egl_display`: The `EGLDisplay` on which the image should be created. + /// - `cursor_overlay`: A boolean flag indicating whether the cursor should be included in the capture. + /// - `output`: Reference to the `WlOutput` from which the frame is to be captured. + /// - `capture_region`: Optional region specifying a sub-area of the output to capture. If `None`, the entire output is captured. + /// + /// # Returns + /// If successful, an EGLImageGuard which contains a pointer 'image' to the created EGLImage + /// On error, the EGL [error code](https://registry.khronos.org/EGL/sdk/docs/man/html/eglGetError.xhtml) is returned via this crates Error type + pub fn capture_output_frame_eglimage_on_display<'a, T: khronos_egl::api::EGL1_5>( &self, - egl_instance: &Instance, + egl_instance: &'a Instance, egl_display: egl::Display, cursor_overlay: bool, output: &WlOutput, capture_region: Option, - ) -> Result { + ) -> Result> { type Attrib = egl::Attrib; let (frame_format, _guard, bo) = self.capture_output_frame_dmabuf(cursor_overlay, output, capture_region)?; + let modifier: u64 = bo.modifier()?.into(); let image_attribs = [ egl::WIDTH as Attrib, frame_format.size.width as Attrib, @@ -269,10 +301,10 @@ impl WayshotConnection { 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, + 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 { @@ -283,7 +315,11 @@ impl WayshotConnection { khronos_egl::ClientBuffer::from_ptr(std::ptr::null_mut()), //NULL &image_attribs, ) { - Ok(image) => Ok(image), + Ok(image) => Ok(EGLImageGuard { + image, + egl_instance, + egl_display, + }), Err(e) => Err(e.into()), } } @@ -291,6 +327,16 @@ impl WayshotConnection { /// 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 + /// The captured frame is returned as a tuple containing the frame format, a guard to manage + /// the WlBuffer's cleanup on drop, and the underlying `BufferObject`. + /// - `cursor_overlay`: A boolean flag indicating whether the cursor should be included in the capture. + /// - `output`: Reference to the `WlOutput` from which the frame is to be captured. + /// - `capture_region`: Optional region specifying a sub-area of the output to capture. If `None`, the entire output is captured. + ///# Returns + /// On success, returns a tuple containing the frame format, + /// a guard to manage the frame's lifecycle, and the GPU-backed `BufferObject`. + /// # Errors + /// - Returns `NoDMAStateError` if the DMA-BUF state is not initialized a the time of initialization of this struct. pub fn capture_output_frame_dmabuf( &self, cursor_overlay: bool, diff --git a/libwayshot/src/screencopy.rs b/libwayshot/src/screencopy.rs index 1bebcd33..9752a17b 100644 --- a/libwayshot/src/screencopy.rs +++ b/libwayshot/src/screencopy.rs @@ -42,6 +42,22 @@ impl Drop for DMAFrameGuard { } } +pub struct EGLImageGuard<'a, T: khronos_egl::api::EGL1_5> { + pub image: khronos_egl::Image, + pub(crate) egl_instance: &'a khronos_egl::Instance, + pub(crate) egl_display: khronos_egl::Display, +} + +impl<'a, T: khronos_egl::api::EGL1_5> Drop for EGLImageGuard<'a, T> { + fn drop(&mut self) { + self.egl_instance + .destroy_image(self.egl_display, self.image) + .unwrap_or_else(|e| { + tracing::error!("EGLimage destruction had error: {e}"); + }); + } +} + /// Type of frame supported by the compositor. For now we only support Argb8888, Xrgb8888, and /// Xbgr8888. /// From 4adb349184415d7dfb3c535fcadedaa09364d548 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Fri, 16 Aug 2024 23:03:19 +0530 Subject: [PATCH 10/21] [fix] libwayshot build error fixed --- Cargo.lock | 1 + libwayshot/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 69ca673d..233c2f74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -612,6 +612,7 @@ dependencies = [ "nix 0.27.1", "thiserror", "tracing", + "wayland-backend", "wayland-client", "wayland-protocols", "wayland-protocols-wlr", diff --git a/libwayshot/Cargo.toml b/libwayshot/Cargo.toml index 991747be..89d39c80 100644 --- a/libwayshot/Cargo.toml +++ b/libwayshot/Cargo.toml @@ -18,6 +18,7 @@ thiserror = "1" wayland-client = "0.31.1" wayland-protocols = { version = "0.31.0", features = ["client", "unstable"] } wayland-protocols-wlr = { version = "0.2.0", features = ["client"] } +wayland-backend = { version = "0.3.3", features = ["client_system"] } gbm = "0.15.0" drm = "0.12.0" From e204fb87defec1897b4dba10c3a3b3ad6ff78190 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Fri, 16 Aug 2024 23:04:11 +0530 Subject: [PATCH 11/21] [fix] remove hardcoded GPU path from libwayshot constructor --- libwayshot/examples/waymirror-egl/src/state.rs | 6 +++++- libwayshot/examples/waymirror.rs | 3 ++- libwayshot/src/lib.rs | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/libwayshot/examples/waymirror-egl/src/state.rs b/libwayshot/examples/waymirror-egl/src/state.rs index 2f93c612..81dc501d 100644 --- a/libwayshot/examples/waymirror-egl/src/state.rs +++ b/libwayshot/examples/waymirror-egl/src/state.rs @@ -70,7 +70,11 @@ impl WaylandEGLState { xdg_surface: None, xdg_toplevel: None, wl_compositor: None, - wayshot: WayshotConnection::from_connection_with_dmabuf(server_connection).unwrap(), + wayshot: WayshotConnection::from_connection_with_dmabuf( + server_connection, + "/dev/dri/renderD128", + ) + .unwrap(), }) } diff --git a/libwayshot/examples/waymirror.rs b/libwayshot/examples/waymirror.rs index c89d7cb6..c350131a 100644 --- a/libwayshot/examples/waymirror.rs +++ b/libwayshot/examples/waymirror.rs @@ -18,7 +18,8 @@ fn main() { let display = conn.display(); display.get_registry(&qhandle, ()); - let wayshot = WayshotConnection::from_connection_with_dmabuf(conn).unwrap(); + let wayshot = + WayshotConnection::from_connection_with_dmabuf(conn, "/dev/dri/renderD128").unwrap(); let mut state = State { wayshot, diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index 4f6e9bcd..e76bf770 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -109,11 +109,11 @@ impl WayshotConnection { Ok(initial_state) } - pub fn from_connection_with_dmabuf(conn: Connection) -> Result { + pub fn from_connection_with_dmabuf(conn: Connection, device_path: &str) -> Result { let (globals, evq) = registry_queue_init::(&conn)?; let linux_dmabuf = globals.bind(&evq.handle(), 4..=ZwpLinuxDmabufV1::interface().version, ())?; - let gpu = dispatch::Card::open("/dev/dri/renderD128"); + let gpu = dispatch::Card::open(device_path); // init a GBM device let gbm = GBMDevice::new(gpu).unwrap(); let mut initial_state = Self { From 3601ce1c5faa086a245cd396d48ce62c0bfdbf65 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Fri, 16 Aug 2024 23:16:39 +0530 Subject: [PATCH 12/21] [fix]] remove reduntant dmabuf_to_texture call from waymirror-egl --- libwayshot/examples/waymirror-egl/src/state.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/libwayshot/examples/waymirror-egl/src/state.rs b/libwayshot/examples/waymirror-egl/src/state.rs index 81dc501d..26cb7fab 100644 --- a/libwayshot/examples/waymirror-egl/src/state.rs +++ b/libwayshot/examples/waymirror-egl/src/state.rs @@ -264,7 +264,6 @@ impl WaylandEGLState { gl::EnableVertexAttribArray(1); gl::BindBuffer(gl::ARRAY_BUFFER, 0); } - self.dmabuf_to_texture(); Ok(()) } From 797ec9ff8a7cc99fef6b74697cb6978e15b6147c Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Fri, 16 Aug 2024 23:39:31 +0530 Subject: [PATCH 13/21] [docs] document WayshotConnection dmabuf constructor --- libwayshot/src/error.rs | 2 +- libwayshot/src/lib.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libwayshot/src/error.rs b/libwayshot/src/error.rs index f01c42c8..49fc5633 100644 --- a/libwayshot/src/error.rs +++ b/libwayshot/src/error.rs @@ -36,7 +36,7 @@ pub enum Error { ProtocolNotFound(String), #[error("error occurred in freeze callback")] FreezeCallbackError, - #[error("dmabuf configuration not initialized")] + #[error("dmabuf configuration not initialized. Did you not use Wayshot::from_connection_with_dmabuf()?")] NoDMAStateError, #[error("dmabuf color format provided by compositor is invalid")] UnrecognizedColorCode(#[from] UnrecognizedFourcc), diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index e76bf770..bc056c6f 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -109,6 +109,11 @@ impl WayshotConnection { Ok(initial_state) } + ///Create a WayshotConnection struct having DMA-BUF support + /// Using this connection is required to make use of the dmabuf functions + ///# Parameters + /// - conn: a Wayland connection + /// - device_path: string pointing to the DRI device that is to be used for creating the DMA-BUFs on. For example: "/dev/dri/renderD128" pub fn from_connection_with_dmabuf(conn: Connection, device_path: &str) -> Result { let (globals, evq) = registry_queue_init::(&conn)?; let linux_dmabuf = From 07ecd65068903e51f3ccc3a543d34669a3185def Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Sat, 17 Aug 2024 22:13:05 +0530 Subject: [PATCH 14/21] [feat] Added helper/wrapper function to convert screencapture EGLImages into GL textures --- Cargo.lock | 1 - libwayshot/Cargo.toml | 1 - .../examples/waymirror-egl/src/state.rs | 29 ++++-------- libwayshot/src/error.rs | 2 + libwayshot/src/lib.rs | 45 ++++++++++++++++++- 5 files changed, 54 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 233c2f74..dc0e6150 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -605,7 +605,6 @@ dependencies = [ "drm", "gbm", "gl", - "gl_loader", "image", "khronos-egl", "memmap2", diff --git a/libwayshot/Cargo.toml b/libwayshot/Cargo.toml index 89d39c80..357622dc 100644 --- a/libwayshot/Cargo.toml +++ b/libwayshot/Cargo.toml @@ -24,5 +24,4 @@ gbm = "0.15.0" 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/src/state.rs b/libwayshot/examples/waymirror-egl/src/state.rs index 26cb7fab..61af76e3 100644 --- a/libwayshot/examples/waymirror-egl/src/state.rs +++ b/libwayshot/examples/waymirror-egl/src/state.rs @@ -3,7 +3,7 @@ use crate::utils::load_shader; use libwayshot::WayshotConnection; -use gl::types::{GLeglImageOES, GLuint}; +use gl::types::GLuint; use khronos_egl::{self as egl}; use std::{ffi::c_void, rc::Rc}; use wayland_client::{ @@ -279,30 +279,17 @@ impl WaylandEGLState { } pub fn dmabuf_to_texture(&self) { - let eglimage_guard = self - .wayshot - .capture_output_frame_eglimage( - &self.egl, - true, - &self.wayshot.get_all_outputs()[0].wl_output, - None, - ) - .unwrap(); unsafe { - let gl_egl_image_texture_target_2d_oes: unsafe extern "system" fn( - target: gl::types::GLenum, - image: GLeglImageOES, - ) -> () = std::mem::transmute( - self.egl - .get_proc_address("glEGLImageTargetTexture2DOES") - .unwrap(), - ); - gl::BindTexture(gl::TEXTURE_2D, self.gl_texture); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); - gl_egl_image_texture_target_2d_oes(gl::TEXTURE_2D, eglimage_guard.image.as_ptr()); + self.wayshot + .bind_output_frame_to_gl_texture( + true, + &self.wayshot.get_all_outputs()[0].wl_output, + None, + ) + .unwrap(); } } diff --git a/libwayshot/src/error.rs b/libwayshot/src/error.rs index 49fc5633..10d87b4f 100644 --- a/libwayshot/src/error.rs +++ b/libwayshot/src/error.rs @@ -46,4 +46,6 @@ pub enum Error { GBMBoFdError(#[from] FdError), #[error(" EGLImage import from dmabuf failed: {0}")] EGLError(#[from] khronos_egl::Error), + #[error("No EGLImageTargetTexture2DOES function located, this extension may not be supported")] + EGLImageToTexProcNotFoundError, } diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index bc056c6f..1d8c0ff0 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -227,6 +227,49 @@ impl WayshotConnection { Ok((frame_format, frame_guard)) } + /// Helper function/wrapper that uses the OpenGL extension OES_EGL_image to convert the EGLImage obtained from [`WayshotConnection::capture_output_frame_eglimage`] + /// into a OpenGL texture. + /// - The caller is supposed to setup everything required for the texture binding. An example call may look like: + /// ``` + /// gl::BindTexture(gl::TEXTURE_2D, self.gl_texture); + /// gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); + /// wayshot_conn + /// .bind_output_frame_to_gl_texture( + /// true, + /// &wayshot_conn.get_all_outputs()[0].wl_output, + /// None) + ///``` + /// # Parameters + /// - `cursor_overlay`: A boolean flag indicating whether the cursor should be included in the capture. + /// - `output`: Reference to the `WlOutput` from which the frame is to be captured. + /// - `capture_region`: Optional region specifying a sub-area of the output to capture. If `None`, the entire output is captured. + /// # Returns + /// - If the function was found and called, an OK(()), note that this does not neccesarily mean that binding was successful, only that the function was called. + /// The caller may check for any OpenGL errors using the standard routes. + /// - If the function was not found, [`Error::EGLImageToTexProcNotFoundError`] is returned + pub unsafe fn bind_output_frame_to_gl_texture( + &self, + cursor_overlay: bool, + output: &WlOutput, + capture_region: Option, + ) -> Result<()> { + let egl = khronos_egl::Instance::new(egl::Static); + let eglimage_guard = + self.capture_output_frame_eglimage(&egl, cursor_overlay, output, capture_region)?; + unsafe { + let gl_egl_image_texture_target_2d_oes: unsafe extern "system" fn( + target: gl::types::GLenum, + image: gl::types::GLeglImageOES, + ) -> () = + std::mem::transmute(match egl.get_proc_address("glEGLImageTargetTexture2DOES") { + Some(a) => a, + None => return Err(Error::EGLImageToTexProcNotFoundError), + }); + + gl_egl_image_texture_target_2d_oes(gl::TEXTURE_2D, eglimage_guard.image.as_ptr()); + Ok(()) + } + } /// Obtain a screencapture in the form of a EGLImage. /// The display on which this image is created is obtained from the Wayland Connection. @@ -234,7 +277,7 @@ impl WayshotConnection { /// It returns the captured frame as an `EGLImage`, wrapped in an `EGLImageGuard` /// for safe handling and cleanup. /// # Parameters - /// - `egl_instance`: Reference to an `EGL1_5` instance, which is used to create the `EGLImage`. + /// - `egl_instance`: Reference to an egl API instance obtained from the khronos_egl crate, which is used to create the `EGLImage`. /// - `cursor_overlay`: A boolean flag indicating whether the cursor should be included in the capture. /// - `output`: Reference to the `WlOutput` from which the frame is to be captured. /// - `capture_region`: Optional region specifying a sub-area of the output to capture. If `None`, the entire output is captured. From 4f9fed6e25dbcbe1557f77b6cd71574bf393cdac Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Sun, 25 Aug 2024 11:07:56 +0530 Subject: [PATCH 15/21] [fix] improved logging in dmabuf API code --- libwayshot/examples/waymirror-egl/src/main.rs | 3 +- libwayshot/src/lib.rs | 38 ++++++++++++++----- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/libwayshot/examples/waymirror-egl/src/main.rs b/libwayshot/examples/waymirror-egl/src/main.rs index 8af37432..de4800d6 100644 --- a/libwayshot/examples/waymirror-egl/src/main.rs +++ b/libwayshot/examples/waymirror-egl/src/main.rs @@ -5,11 +5,10 @@ mod utils; use error::Result; use state::WaylandEGLState; -use std::str::FromStr; pub fn main() -> Result<(), Box> { tracing_subscriber::fmt() - .with_max_level(tracing::Level::from_str("error")?) + .with_max_level(tracing::Level::TRACE) .with_writer(std::io::stderr) .init(); diff --git a/libwayshot/src/lib.rs b/libwayshot/src/lib.rs index 1d8c0ff0..abd32b44 100644 --- a/libwayshot/src/lib.rs +++ b/libwayshot/src/lib.rs @@ -262,11 +262,18 @@ impl WayshotConnection { image: gl::types::GLeglImageOES, ) -> () = std::mem::transmute(match egl.get_proc_address("glEGLImageTargetTexture2DOES") { - Some(a) => a, - None => return Err(Error::EGLImageToTexProcNotFoundError), + Some(f) => { + tracing::debug!("glEGLImageTargetTexture2DOES found at address {:#?}", f); + f + } + None => { + tracing::error!("glEGLImageTargetTexture2DOES not found"); + return Err(Error::EGLImageToTexProcNotFoundError); + } }); gl_egl_image_texture_target_2d_oes(gl::TEXTURE_2D, eglimage_guard.image.as_ptr()); + tracing::trace!("glEGLImageTargetTexture2DOES called"); Ok(()) } } @@ -298,6 +305,7 @@ impl WayshotConnection { None => return Err(egl_instance.get_error().unwrap().into()), } }; + tracing::trace!("eglDisplay obtained from Wayland connection's display"); egl_instance.initialize(egl_display)?; self.capture_output_frame_eglimage_on_display( @@ -355,6 +363,10 @@ impl WayshotConnection { (modifier >> 32) as Attrib, egl::ATTRIB_NONE as Attrib, ]; + tracing::debug!( + "Calling eglCreateImage with attributes: {:#?}", + image_attribs + ); unsafe { match egl_instance.create_image( egl_display, @@ -368,7 +380,10 @@ impl WayshotConnection { egl_instance, egl_display, }), - Err(e) => Err(e.into()), + Err(e) => { + tracing::error!("eglCreateImage call failed with error {e}"); + Err(e.into()) + } } } } @@ -406,9 +421,14 @@ impl WayshotConnection { gbm::Format::try_from(frame_format.format)?, BufferObjectFlags::RENDERING | BufferObjectFlags::LINEAR, )?; + let stride = bo.stride()?; let modifier: u64 = bo.modifier()?.into(); - + tracing::debug!( + "Created GBM Buffer object with input frame format {:#?}, stride {:#?} and modifier {:#?} ", + frame_format, + stride,modifier + ); let frame_guard = self.capture_output_frame_inner_dmabuf( state, event_queue, @@ -461,7 +481,7 @@ impl WayshotConnection { } }; - debug!("Capturing output..."); + tracing::debug!("Capturing output(shm buffer)..."); let frame = if let Some(embedded_region) = capture_region { screencopy_manager.capture_output_region( cursor_overlay, @@ -552,7 +572,7 @@ impl WayshotConnection { } }; - debug!("Capturing output..."); + tracing::debug!("Capturing output for DMA-BUF API..."); let frame = if let Some(embedded_region) = capture_region { screencopy_manager.capture_output_region( cursor_overlay, @@ -605,6 +625,7 @@ impl WayshotConnection { let dma_height = frame_format.size.height; let dma_params = linux_dmabuf.create_params(&qh, ()); + dma_params.add( fd.as_fd(), 0, @@ -613,6 +634,7 @@ impl WayshotConnection { (modifier >> 32) as u32, (modifier & 0xffffffff) as u32, ); + tracing::trace!("Called ZwpLinuxBufferParamsV1::create_params "); let dmabuf_wlbuf = dma_params.create_immed( dma_width as i32, dma_height as i32, @@ -621,13 +643,11 @@ impl WayshotConnection { &qh, (), ); - + tracing::trace!("Called ZwpLinuxBufferParamsV1::create_immed to create WlBuffer "); // Copy the pixel data advertised by the compositor into the buffer we just created. frame.copy(&dmabuf_wlbuf); tracing::debug!("wlr-screencopy copy() with dmabuf complete"); - //dma_params.destroy(); - //linux_dmabuf.destroy(); // On copy the Ready / Failed events are fired by the frame object, so here we check for them. loop { // Basically reads, if frame state is not None then... From bf3fe1bf1ecf39905fc18d9079da6a102622b66d Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Sun, 25 Aug 2024 20:56:28 +0530 Subject: [PATCH 16/21] [doc] update waymirror-egl Readme --- libwayshot/examples/waymirror-egl/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libwayshot/examples/waymirror-egl/README.md b/libwayshot/examples/waymirror-egl/README.md index ad3b0d01..69d4fe45 100644 --- a/libwayshot/examples/waymirror-egl/README.md +++ b/libwayshot/examples/waymirror-egl/README.md @@ -1,7 +1,8 @@ -# wayland-egl-ctx +# waymirror-egl +Example code for using the libwayshot DMA-BUF GL screencapture pipeline. -Example wayland xdg window driven using egl +This example sets up an EGL+OpenGL context, sets up libwayshot and renders the main display onto a rectangle after converting the screencapture into a texture. -# TODO +Adapted from https://github.com/Shinyzenith/wayland-egl-ctx, all credits to @Shinyzenith -- [x] Get GL calls to work +Run using `cargo run` inside this directory. From 4d5a24298e97f77dc5afead3854cb2a75f829e3d Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Sun, 25 Aug 2024 21:00:18 +0530 Subject: [PATCH 17/21] [fix] change logging level in waymirror-egl to Debug --- libwayshot/examples/waymirror-egl/src/main.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libwayshot/examples/waymirror-egl/src/main.rs b/libwayshot/examples/waymirror-egl/src/main.rs index de4800d6..969851a5 100644 --- a/libwayshot/examples/waymirror-egl/src/main.rs +++ b/libwayshot/examples/waymirror-egl/src/main.rs @@ -8,7 +8,7 @@ use state::WaylandEGLState; pub fn main() -> Result<(), Box> { tracing_subscriber::fmt() - .with_max_level(tracing::Level::TRACE) + .with_max_level(tracing::Level::DEBUG) .with_writer(std::io::stderr) .init(); @@ -51,13 +51,12 @@ pub fn main() -> Result<(), Box> { state.init_egl()?; while state.running { event_queue.dispatch_pending(&mut state)?; - // event_queue.blocking_dispatch(&mut state)?; state.draw(); state .egl .swap_buffers(state.egl_display.unwrap(), state.egl_surface.unwrap())?; - //tracing::event!(tracing::Level::DEBUG, "eglSwapBuffers called"); + tracing::trace!("eglSwapBuffers called"); } state.deinit()?; From c56e31156bbc6af20bd4a72193ec2d2da8f5f76d Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Sun, 25 Aug 2024 21:05:26 +0530 Subject: [PATCH 18/21] [fix] remove unnecessary .gitignore in waymirror-egl --- libwayshot/examples/waymirror-egl/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 libwayshot/examples/waymirror-egl/.gitignore diff --git a/libwayshot/examples/waymirror-egl/.gitignore b/libwayshot/examples/waymirror-egl/.gitignore deleted file mode 100644 index ea8c4bf7..00000000 --- a/libwayshot/examples/waymirror-egl/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target From 21716e7daf1962d19b239a2451eced65d9b4a517 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Sun, 25 Aug 2024 21:42:08 +0530 Subject: [PATCH 19/21] [ci/cd] attempting to fix the build --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 49f07bab..32519385 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,12 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 + + - name: Install wayland dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libwayland-dev \ - name: Build run: | From 76c3e3233835ba9adbca16d61c39cf2a974a26c5 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Sun, 25 Aug 2024 21:45:59 +0530 Subject: [PATCH 20/21] [ci\cd] add libegl system deps to fix github CI --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 32519385..a9505f7f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ jobs: sudo apt-get update sudo apt-get install -y --no-install-recommends \ libwayland-dev \ + libegl-dev \ - name: Build run: | From f93878ad8cac41a4eba788b683a15ea387e8dfc6 Mon Sep 17 00:00:00 2001 From: CheerfulPianissimo Date: Sun, 25 Aug 2024 21:49:47 +0530 Subject: [PATCH 21/21] [fix] remove unused egl_image struct field in Waymirror demo --- libwayshot/examples/waymirror-egl/src/state.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/libwayshot/examples/waymirror-egl/src/state.rs b/libwayshot/examples/waymirror-egl/src/state.rs index 61af76e3..6b4a8022 100644 --- a/libwayshot/examples/waymirror-egl/src/state.rs +++ b/libwayshot/examples/waymirror-egl/src/state.rs @@ -29,7 +29,6 @@ pub struct WaylandEGLState { pub egl_display: Option, pub egl_surface: Option, pub egl_context: Option, - pub egl_image: Option, pub gl_program: GLuint, pub gl_texture: GLuint, @@ -62,7 +61,6 @@ impl WaylandEGLState { egl_display: None, egl_surface: None, egl_context: None, - egl_image: None, gl_program: 0, gl_texture: 0,