diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/pw_utils.rs | 200 |
1 files changed, 122 insertions, 78 deletions
diff --git a/src/pw_utils.rs b/src/pw_utils.rs index 541c9084..f1416134 100644 --- a/src/pw_utils.rs +++ b/src/pw_utils.rs @@ -4,6 +4,7 @@ use std::io::Cursor; use std::iter::zip; use std::mem; use std::os::fd::{AsFd, AsRawFd, BorrowedFd}; +use std::ptr::NonNull; use std::rc::Rc; use std::time::Duration; @@ -28,6 +29,7 @@ use pipewire::spa::utils::{ }; use pipewire::spa::{self}; use pipewire::stream::{Stream, StreamFlags, StreamListener, StreamState}; +use pipewire::sys::{pw_buffer, pw_stream_queue_buffer}; use smithay::backend::allocator::dmabuf::{AsDmabuf, Dmabuf}; use smithay::backend::allocator::format::FormatSet; use smithay::backend::allocator::gbm::{GbmBuffer, GbmBufferFlags, GbmDevice}; @@ -838,6 +840,10 @@ impl Cast { } } + fn dequeue_available_buffer(&mut self) -> Option<NonNull<pw_buffer>> { + unsafe { NonNull::new(self.stream.dequeue_raw_buffer()) } + } + pub fn dequeue_buffer_and_render( &mut self, renderer: &mut GlesRenderer, @@ -868,57 +874,70 @@ impl Cast { trace!("no damage, skipping frame"); return false; } + drop(inner); - let Some(mut buffer) = self.stream.dequeue_buffer() else { + let Some(pw_buffer) = self.dequeue_available_buffer() else { warn!("no available buffer in pw stream, skipping frame"); return false; }; - - let fd = buffer.datas_mut()[0].as_raw().fd; - let dmabuf = &inner.dmabufs[&fd]; - - match render_to_dmabuf( - renderer, - dmabuf.clone(), - size, - scale, - Transform::Normal, - elements.iter().rev(), - ) { - Ok(sync_point) => { - // FIXME: implement PipeWire explicit sync, and at the very least async wait. - if wait_for_sync { - let _span = tracy_client::span!("wait for completion"); - if let Err(err) = sync_point.wait() { - warn!("error waiting for pw frame completion: {err:?}"); + let buffer = pw_buffer.as_ptr(); + + let inner = self.inner.borrow_mut(); + unsafe { + let spa_buffer = (*buffer).buffer; + + let fd = (*(*spa_buffer).datas).fd; + let dmabuf = &inner.dmabufs[&fd]; + + match render_to_dmabuf( + renderer, + dmabuf.clone(), + size, + scale, + Transform::Normal, + elements.iter().rev(), + ) { + Ok(sync_point) => { + // FIXME: implement PipeWire explicit sync, and at the very least async wait. + if wait_for_sync { + let _span = tracy_client::span!("wait for completion"); + if let Err(err) = sync_point.wait() { + warn!("error waiting for pw frame completion: {err:?}"); + } } } + Err(err) => { + warn!("error rendering to dmabuf: {err:?}"); + return_unused_buffer(&self.stream, pw_buffer); + return false; + } } - Err(err) => { - warn!("error rendering to dmabuf: {err:?}"); - return false; - } - } - for (data, (stride, offset)) in - zip(buffer.datas_mut(), zip(dmabuf.strides(), dmabuf.offsets())) - { - let chunk = data.chunk_mut(); - // With DMA-BUFs, consumers should ignore the size field, and producers are allowed to - // set it to 0. - // - // https://docs.pipewire.org/page_dma_buf.html - // - // However, OBS checks for size != 0 as a workaround for old compositor versions, - // so we set it to 1. - *chunk.size_mut() = 1; - *chunk.stride_mut() = stride as i32; - *chunk.offset_mut() = offset; + for (i, (stride, offset)) in zip(dmabuf.strides(), dmabuf.offsets()).enumerate() { + let spa_data = (*spa_buffer).datas.add(i); + let chunk = (*spa_data).chunk; + + // With DMA-BUFs, consumers should ignore the size field, and producers are allowed + // to set it to 0. + // + // https://docs.pipewire.org/page_dma_buf.html + // + // However, OBS checks for size != 0 as a workaround for old compositor versions, + // so we set it to 1. + (*chunk).size = 1; + // Clear the corrupted flag we may have set before. + (*chunk).flags = SPA_CHUNK_FLAG_NONE as i32; + + (*chunk).stride = stride as i32; + (*chunk).offset = offset; + + trace!( + "pw buffer: fd = {}, stride = {stride}, offset = {offset}", + (*spa_data).fd + ); + } - trace!( - "pw buffer: fd = {}, stride = {stride}, offset = {offset}", - data.as_raw().fd - ); + pw_stream_queue_buffer(self.stream.as_raw_ptr(), buffer); } true @@ -935,50 +954,63 @@ impl Cast { if let CastState::Ready { damage_tracker, .. } = &mut inner.state { *damage_tracker = None; }; + drop(inner); - let Some(mut buffer) = self.stream.dequeue_buffer() else { - warn!("no available buffer in pw stream, skipping clear"); + let Some(pw_buffer) = self.dequeue_available_buffer() else { + warn!("no available buffer in pw stream, skipping frame"); return false; }; - - let fd = buffer.datas_mut()[0].as_raw().fd; - let dmabuf = &inner.dmabufs[&fd]; - - match clear_dmabuf(renderer, dmabuf.clone()) { - Ok(sync_point) => { - // FIXME: implement PipeWire explicit sync, and at the very least async wait. - if wait_for_sync { - let _span = tracy_client::span!("wait for completion"); - if let Err(err) = sync_point.wait() { - warn!("error waiting for pw frame completion: {err:?}"); + let buffer = pw_buffer.as_ptr(); + + let inner = self.inner.borrow_mut(); + unsafe { + let spa_buffer = (*buffer).buffer; + + let fd = (*(*spa_buffer).datas).fd; + let dmabuf = &inner.dmabufs[&fd]; + + match clear_dmabuf(renderer, dmabuf.clone()) { + Ok(sync_point) => { + // FIXME: implement PipeWire explicit sync, and at the very least async wait. + if wait_for_sync { + let _span = tracy_client::span!("wait for completion"); + if let Err(err) = sync_point.wait() { + warn!("error waiting for pw frame completion: {err:?}"); + } } } + Err(err) => { + warn!("error clearing dmabuf: {err:?}"); + return_unused_buffer(&self.stream, pw_buffer); + return false; + } } - Err(err) => { - warn!("error clearing dmabuf: {err:?}"); - return false; - } - } - for (data, (stride, offset)) in - zip(buffer.datas_mut(), zip(dmabuf.strides(), dmabuf.offsets())) - { - let chunk = data.chunk_mut(); - // With DMA-BUFs, consumers should ignore the size field, and producers are allowed to - // set it to 0. - // - // https://docs.pipewire.org/page_dma_buf.html - // - // However, OBS checks for size != 0 as a workaround for old compositor versions, - // so we set it to 1. - *chunk.size_mut() = 1; - *chunk.stride_mut() = stride as i32; - *chunk.offset_mut() = offset; + for (i, (stride, offset)) in zip(dmabuf.strides(), dmabuf.offsets()).enumerate() { + let spa_data = (*spa_buffer).datas.add(i); + let chunk = (*spa_data).chunk; + + // With DMA-BUFs, consumers should ignore the size field, and producers are allowed + // to set it to 0. + // + // https://docs.pipewire.org/page_dma_buf.html + // + // However, OBS checks for size != 0 as a workaround for old compositor versions, + // so we set it to 1. + (*chunk).size = 1; + // Clear the corrupted flag we may have set before. + (*chunk).flags = SPA_CHUNK_FLAG_NONE as i32; + + (*chunk).stride = stride as i32; + (*chunk).offset = offset; + + trace!( + "pw buffer: fd = {}, stride = {stride}, offset = {offset}", + (*spa_data).fd + ); + } - trace!( - "pw buffer: fd = {}, stride = {stride}, offset = {offset}", - data.as_raw().fd - ); + pw_stream_queue_buffer(self.stream.as_raw_ptr(), buffer); } true @@ -1151,3 +1183,15 @@ fn allocate_dmabuf( .context("error exporting GBM buffer object as dmabuf")?; Ok(dmabuf) } + +unsafe fn return_unused_buffer(stream: &Stream, pw_buffer: NonNull<pw_buffer>) { + // pw_stream_return_buffer() requires too new PipeWire (1.4.0). So, mark as + // corrupted and queue. + let pw_buffer = pw_buffer.as_ptr(); + let spa_buffer = (*pw_buffer).buffer; + let chunk = (*(*spa_buffer).datas).chunk; + // Some (older?) consumers will check for size == 0 instead of the CORRUPTED flag. + (*chunk).size = 0; + (*chunk).flags = SPA_CHUNK_FLAG_CORRUPTED as i32; + pw_stream_queue_buffer(stream.as_raw_ptr(), pw_buffer); +} |
