Re-use screencopy session instead of creating one each frame

This commit is contained in:
Ian Douglas Scott 2024-01-24 15:29:03 -08:00
parent 5269356089
commit 14d2a66c9d
4 changed files with 118 additions and 78 deletions

View file

@ -322,7 +322,6 @@ impl App {
if let Some(sender) = self.wayland_cmd_sender.as_ref() { if let Some(sender) = self.wayland_cmd_sender.as_ref() {
let mut capture_filter = wayland::CaptureFilter::default(); let mut capture_filter = wayland::CaptureFilter::default();
if self.visible { if self.visible {
// XXX handle on wrong connection
capture_filter.workspaces_on_outputs = capture_filter.workspaces_on_outputs =
self.outputs.iter().map(|x| x.handle.clone()).collect(); self.outputs.iter().map(|x| x.handle.clone()).collect();
capture_filter.toplevels_on_workspaces = self capture_filter.toplevels_on_workspaces = self

View file

@ -5,7 +5,7 @@ use cctk::{
workspace::v1::client::zcosmic_workspace_handle_v1, workspace::v1::client::zcosmic_workspace_handle_v1,
}, },
screencopy::{ScreencopySessionData, ScreencopySessionDataExt}, screencopy::{ScreencopySessionData, ScreencopySessionDataExt},
wayland_client::{protocol::wl_output, Proxy, QueueHandle}, wayland_client::{protocol::wl_output, Connection, Proxy, QueueHandle},
}; };
use cosmic::cctk; use cosmic::cctk;
@ -37,75 +37,119 @@ pub struct Capture {
pub source: CaptureSource, pub source: CaptureSource,
first_frame: AtomicBool, first_frame: AtomicBool,
running: AtomicBool, running: AtomicBool,
capturing: AtomicBool,
session: zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1,
} }
impl Capture { impl Capture {
pub fn new(source: CaptureSource) -> Capture { pub fn new(
Capture { source: CaptureSource,
buffer: Mutex::new(None), manager: &zcosmic_screencopy_manager_v1::ZcosmicScreencopyManagerV1,
source, qh: &QueueHandle<AppData>,
first_frame: AtomicBool::new(true), ) -> Arc<Capture> {
running: AtomicBool::new(false), Arc::new_cyclic(|weak_capture| {
} let udata = SessionData {
session_data: Default::default(),
capture: weak_capture.clone(),
};
let session = match &source {
CaptureSource::Toplevel(toplevel) => manager.capture_toplevel(
toplevel,
zcosmic_screencopy_manager_v1::CursorMode::Hidden,
qh,
udata,
),
CaptureSource::Workspace(workspace, output) => manager.capture_workspace(
workspace,
output,
zcosmic_screencopy_manager_v1::CursorMode::Hidden,
qh,
udata,
),
};
Capture {
buffer: Mutex::new(None),
source,
first_frame: AtomicBool::new(true),
running: AtomicBool::new(false),
capturing: AtomicBool::new(false),
session,
}
})
} }
// Returns `None` if capture is no longer active // Returns `None` if capture is destroyed
// (or if `session` wasn't created with `SessionData`) // (or if `session` wasn't created with `SessionData`)
pub fn for_session( pub fn for_session(
session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1, session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1,
) -> Option<Arc<Self>> { ) -> Option<Arc<Self>> {
session session.data::<SessionData>()?.capture.upgrade()
.data::<SessionData>()?
.capture
.upgrade()
.filter(|c| c.running())
} }
pub fn running(&self) -> bool { pub fn running(&self) -> bool {
self.running.load(Ordering::SeqCst) self.running.load(Ordering::SeqCst)
} }
// Buffer is currently attached and commited for capture by server
pub fn capturing(&self) -> bool {
self.capturing.load(Ordering::SeqCst)
}
pub fn set_capturing(&self, value: bool) {
if value {
self.first_frame.store(false, Ordering::SeqCst);
}
self.capturing.store(value, Ordering::SeqCst);
}
pub fn first_frame(&self) -> bool { pub fn first_frame(&self) -> bool {
self.first_frame.load(Ordering::SeqCst) self.first_frame.load(Ordering::SeqCst)
} }
pub fn cancel(&self) { // Start capturing frames
self.running.store(false, Ordering::SeqCst); pub fn start(&self, conn: &Connection) {
*self.buffer.lock().unwrap() = None; let already_running = self.running.swap(true, Ordering::SeqCst);
let have_buffer = self.buffer.lock().unwrap().is_some();
if have_buffer && !already_running {
self.attach_buffer_and_commit(conn);
}
} }
pub fn capture( // Stop capturing. Can be started again with `start`
self: &Arc<Self>, pub fn stop(&self) {
manager: &zcosmic_screencopy_manager_v1::ZcosmicScreencopyManagerV1, self.running.store(false, Ordering::SeqCst);
qh: &QueueHandle<AppData>, self.first_frame.store(true, Ordering::SeqCst);
) { // TODO: Reallocate buffers on re-start
// Mark as running. If already running, this is not the first frame. // *self.buffer.lock().unwrap() = None;
let already_running = self.running.swap(true, Ordering::SeqCst); }
self.first_frame.store(!already_running, Ordering::SeqCst);
let udata = SessionData { pub fn attach_buffer_and_commit(&self, conn: &Connection) {
session_data: Default::default(), let buffer = self.buffer.lock().unwrap();
capture: Arc::downgrade(self), let buffer = buffer.as_ref().unwrap();
};
match &self.source { let node = buffer
CaptureSource::Toplevel(toplevel) => { .node()
manager.capture_toplevel( .and_then(|x| x.to_str().map(|x| x.to_string()));
toplevel,
zcosmic_screencopy_manager_v1::CursorMode::Hidden, self.session.attach_buffer(&buffer.buffer, node, 0); // XXX age?
qh, if self.first_frame() {
udata, self.session
); .commit(zcosmic_screencopy_session_v1::Options::empty());
} } else {
CaptureSource::Workspace(workspace, output) => { self.session
manager.capture_workspace( .commit(zcosmic_screencopy_session_v1::Options::OnDamage);
workspace,
output,
zcosmic_screencopy_manager_v1::CursorMode::Hidden,
qh,
udata,
);
}
} }
conn.flush().unwrap();
self.set_capturing(true);
}
}
impl Drop for Capture {
fn drop(&mut self) {
self.session.destroy();
} }
} }

View file

@ -1,10 +1,6 @@
// Workspaces Info, Toplevel Info // A thread handles screencopy, and other wayland protocols, returning information as a
// Capture // subscription.
// - subscribe to all workspaces, to start with? All that are associated with an output should be
// shown on one.
// * Need output name to compare?
// TODO: Way to activate workspace, toplevel? Close? Move?
use cctk::{ use cctk::{
cosmic_protocols::{ cosmic_protocols::{
toplevel_info::v1::client::zcosmic_toplevel_handle_v1, toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
@ -99,6 +95,7 @@ pub enum Cmd {
} }
pub struct AppData { pub struct AppData {
conn: Connection,
qh: QueueHandle<Self>, qh: QueueHandle<Self>,
dmabuf_state: DmabufState, dmabuf_state: DmabufState,
registry_state: RegistryState, registry_state: RegistryState,
@ -151,10 +148,10 @@ impl AppData {
for (source, capture) in self.captures.borrow_mut().iter_mut() { for (source, capture) in self.captures.borrow_mut().iter_mut() {
let matches = self.matches_capture_filter(source); let matches = self.matches_capture_filter(source);
let running = capture.running(); let running = capture.running();
if running && !matches { if matches {
capture.cancel(); capture.start(&self.conn);
} else if !running & matches { } else {
capture.capture(&self.screencopy_state.screencopy_manager, &self.qh); capture.stop();
} }
} }
} }
@ -165,9 +162,10 @@ impl AppData {
.entry(source.clone()) .entry(source.clone())
.or_insert_with(|| { .or_insert_with(|| {
let matches = self.matches_capture_filter(&source); let matches = self.matches_capture_filter(&source);
let capture = Arc::new(Capture::new(source)); let capture =
Capture::new(source, &self.screencopy_state.screencopy_manager, &self.qh);
if matches { if matches {
capture.capture(&self.screencopy_state.screencopy_manager, &self.qh); capture.start(&self.conn);
} }
capture capture
}); });
@ -175,7 +173,7 @@ impl AppData {
fn remove_capture_source(&self, source: CaptureSource) { fn remove_capture_source(&self, source: CaptureSource) {
if let Some(capture) = self.captures.borrow_mut().remove(&source) { if let Some(capture) = self.captures.borrow_mut().remove(&source) {
capture.cancel(); capture.stop();
} }
} }
} }
@ -240,6 +238,7 @@ fn start(conn: Connection) -> mpsc::Receiver<Event> {
let registry_state = RegistryState::new(&globals); let registry_state = RegistryState::new(&globals);
let mut app_data = AppData { let mut app_data = AppData {
conn: conn.clone(),
qh: qh.clone(), qh: qh.clone(),
dmabuf_state, dmabuf_state,
workspace_state: WorkspaceState::new(&registry_state, &qh), // Create before toplevel info state workspace_state: WorkspaceState::new(&registry_state, &qh), // Create before toplevel info state

View file

@ -20,7 +20,6 @@ impl ScreencopyHandler for AppData {
buffer_infos: &[BufferInfo], buffer_infos: &[BufferInfo],
) { ) {
let Some(capture) = Capture::for_session(session) else { let Some(capture) = Capture::for_session(session) else {
session.destroy();
return; return;
}; };
@ -32,30 +31,26 @@ impl ScreencopyHandler for AppData {
{ {
*buffer = Some(self.create_buffer(buffer_infos)); *buffer = Some(self.create_buffer(buffer_infos));
} }
let buffer = buffer.as_ref().unwrap();
let node = buffer drop(buffer);
.node()
.and_then(|x| x.to_str().map(|x| x.to_string())); if !capture.running() {
session.attach_buffer(&buffer.buffer, node, 0); // XXX age? capture.attach_buffer_and_commit(conn);
if capture.first_frame() {
session.commit(zcosmic_screencopy_session_v1::Options::empty());
} else {
session.commit(zcosmic_screencopy_session_v1::Options::OnDamage);
} }
conn.flush().unwrap();
} }
fn ready( fn ready(
&mut self, &mut self,
_conn: &Connection, conn: &Connection,
_qh: &QueueHandle<Self>, _qh: &QueueHandle<Self>,
session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1, session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1,
) { ) {
let Some(capture) = Capture::for_session(session) else { let Some(capture) = Capture::for_session(session) else {
session.destroy();
return; return;
}; };
if !capture.running() {
return;
}
let mut buffer = capture.buffer.lock().unwrap(); let mut buffer = capture.buffer.lock().unwrap();
if buffer.is_none() { if buffer.is_none() {
@ -76,10 +71,13 @@ impl ScreencopyHandler for AppData {
)); ));
} }
}; };
session.destroy();
capture.set_capturing(false);
drop(buffer);
// Capture again on damage // Capture again on damage
capture.capture(&self.screencopy_state.screencopy_manager, &self.qh); capture.attach_buffer_and_commit(conn);
} }
fn failed( fn failed(
@ -92,8 +90,8 @@ impl ScreencopyHandler for AppData {
// TODO // TODO
println!("Failed"); println!("Failed");
if let Some(capture) = Capture::for_session(session) { if let Some(capture) = Capture::for_session(session) {
capture.cancel(); capture.set_capturing(false);
capture.stop();
} }
session.destroy();
} }
} }