diff options
| author | sodiboo <37938646+sodiboo@users.noreply.github.com> | 2024-03-08 13:10:55 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-03-08 04:10:55 -0800 |
| commit | ca22e70cc4a868fdb4dec2790ec71fb9a2cfb6bd (patch) | |
| tree | 69373c13068005180cc75372e16cf493dcb98e38 /src/render_helpers | |
| parent | 1a784e6e66785f360da6df59ae2fb4b98370ca3c (diff) | |
| download | niri-ca22e70cc4a868fdb4dec2790ec71fb9a2cfb6bd.tar.gz niri-ca22e70cc4a868fdb4dec2790ec71fb9a2cfb6bd.tar.bz2 niri-ca22e70cc4a868fdb4dec2790ec71fb9a2cfb6bd.zip | |
Implement wlr-screencopy v1 (#243)
* Implement wlr-screencopy
* Finish the implementation
Lots of changes, mainly to fix transform handling. Turns out, grim
expects transformed buffers and untransforms them by itself using info
from wl_output. This means that render helpers needed to learn how to
actually render transformed buffers.
Also, it meant that y_invert is no longer needed.
Next, moved the rendering to the Screencopy frame handler. Turns out,
copy() is more or less expected to return immediately, whereas
copy_with_damage() is expected to wait until the next VBlank. At least
that's the intent I parse reading the protocol.
Finally, brought the version from 3 down to 1, because
copy_with_damage() will need bigger changes. Grim still works, others
not really, mainly because they bind v3 unnecessarily, even if they
don't use the damage request.
---------
Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
Diffstat (limited to 'src/render_helpers')
| -rw-r--r-- | src/render_helpers/mod.rs | 102 | ||||
| -rw-r--r-- | src/render_helpers/offscreen.rs | 1 |
2 files changed, 70 insertions, 33 deletions
diff --git a/src/render_helpers/mod.rs b/src/render_helpers/mod.rs index 7d7ea9c1..02ef2853 100644 --- a/src/render_helpers/mod.rs +++ b/src/render_helpers/mod.rs @@ -1,10 +1,15 @@ -use anyhow::Context; +use std::ptr; + +use anyhow::{ensure, Context}; use smithay::backend::allocator::Fourcc; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::{GlesMapping, GlesRenderer, GlesTexture}; use smithay::backend::renderer::sync::SyncPoint; -use smithay::backend::renderer::{Bind, ExportMem, Frame, Offscreen, Renderer}; +use smithay::backend::renderer::{buffer_dimensions, Bind, ExportMem, Frame, Offscreen, Renderer}; +use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer; +use smithay::reexports::wayland_server::protocol::wl_shm; use smithay::utils::{Physical, Rectangle, Scale, Size, Transform}; +use smithay::wayland::shm; pub mod gradient; pub mod offscreen; @@ -18,12 +23,12 @@ pub fn render_to_texture( renderer: &mut GlesRenderer, size: Size<i32, Physical>, scale: Scale<f64>, + transform: Transform, fourcc: Fourcc, elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>, ) -> anyhow::Result<(GlesTexture, SyncPoint)> { let _span = tracy_client::span!(); - let output_rect = Rectangle::from_loc_and_size((0, 0), size); let buffer_size = size.to_logical(1).to_buffer(1, Transform::Normal); let texture: GlesTexture = renderer @@ -34,27 +39,7 @@ pub fn render_to_texture( .bind(texture.clone()) .context("error binding texture")?; - let mut frame = renderer - .render(size, Transform::Normal) - .context("error starting frame")?; - - frame - .clear([0., 0., 0., 0.], &[output_rect]) - .context("error clearing")?; - - for element in elements { - let src = element.src(); - let dst = element.geometry(scale); - - if let Some(mut damage) = output_rect.intersection(dst) { - damage.loc -= dst.loc; - element - .draw(&mut frame, src, dst, &[damage]) - .context("error drawing element")?; - } - } - - let sync_point = frame.finish().context("error finishing frame")?; + let sync_point = render_elements(renderer, size, scale, transform, elements)?; Ok((texture, sync_point)) } @@ -62,12 +47,13 @@ pub fn render_and_download( renderer: &mut GlesRenderer, size: Size<i32, Physical>, scale: Scale<f64>, + transform: Transform, fourcc: Fourcc, elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>, ) -> anyhow::Result<GlesMapping> { let _span = tracy_client::span!(); - let (_, sync_point) = render_to_texture(renderer, size, scale, fourcc, elements)?; + let (_, sync_point) = render_to_texture(renderer, size, scale, transform, fourcc, elements)?; sync_point.wait(); let buffer_size = size.to_logical(1).to_buffer(1, Transform::Normal); @@ -81,13 +67,14 @@ pub fn render_to_vec( renderer: &mut GlesRenderer, size: Size<i32, Physical>, scale: Scale<f64>, + transform: Transform, fourcc: Fourcc, elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>, ) -> anyhow::Result<Vec<u8>> { let _span = tracy_client::span!(); - let mapping = - render_and_download(renderer, size, scale, fourcc, elements).context("error rendering")?; + let mapping = render_and_download(renderer, size, scale, transform, fourcc, elements) + .context("error rendering")?; let copy = renderer .map_texture(&mapping) .context("error mapping texture")?; @@ -100,15 +87,66 @@ pub fn render_to_dmabuf( dmabuf: smithay::backend::allocator::dmabuf::Dmabuf, size: Size<i32, Physical>, scale: Scale<f64>, + transform: Transform, + elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>, +) -> anyhow::Result<SyncPoint> { + let _span = tracy_client::span!(); + renderer.bind(dmabuf).context("error binding texture")?; + render_elements(renderer, size, scale, transform, elements) +} + +pub fn render_to_shm( + renderer: &mut GlesRenderer, + buffer: &WlBuffer, + scale: Scale<f64>, + transform: Transform, elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>, ) -> anyhow::Result<()> { let _span = tracy_client::span!(); - let output_rect = Rectangle::from_loc_and_size((0, 0), size); + let buffer_size = buffer_dimensions(buffer).context("error getting buffer dimensions")?; + let size = buffer_size.to_logical(1, Transform::Normal).to_physical(1); + + let mapping = + render_and_download(renderer, size, scale, transform, Fourcc::Argb8888, elements)?; + let bytes = renderer + .map_texture(&mapping) + .context("error mapping texture")?; + + shm::with_buffer_contents_mut(buffer, |shm_buffer, shm_len, buffer_data| { + ensure!( + // The buffer prefers pixels in little endian ... + buffer_data.format == wl_shm::Format::Argb8888 + && buffer_data.stride == size.w * 4 + && buffer_data.height == size.h + && shm_len as i32 == buffer_data.stride * buffer_data.height, + "invalid buffer format or size" + ); + + ensure!(bytes.len() == shm_len, "mapped buffer has wrong length"); + + unsafe { + let _span = tracy_client::span!("copy_nonoverlapping"); + ptr::copy_nonoverlapping(bytes.as_ptr(), shm_buffer.cast(), shm_len); + } + + Ok(()) + }) + .context("expected shm buffer, but didn't get one")? +} + +fn render_elements( + renderer: &mut GlesRenderer, + size: Size<i32, Physical>, + scale: Scale<f64>, + transform: Transform, + elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>, +) -> anyhow::Result<SyncPoint> { + let transform = transform.invert(); + let output_rect = Rectangle::from_loc_and_size((0, 0), transform.transform_size(size)); - renderer.bind(dmabuf).context("error binding texture")?; let mut frame = renderer - .render(size, Transform::Normal) + .render(size, transform) .context("error starting frame")?; frame @@ -127,7 +165,5 @@ pub fn render_to_dmabuf( } } - let _sync_point = frame.finish().context("error finishing frame")?; - - Ok(()) + frame.finish().context("error finishing frame") } diff --git a/src/render_helpers/offscreen.rs b/src/render_helpers/offscreen.rs index 69b0be84..72c2471b 100644 --- a/src/render_helpers/offscreen.rs +++ b/src/render_helpers/offscreen.rs @@ -54,6 +54,7 @@ impl OffscreenRenderElement { renderer, geo.size, Scale::from(scale as f64), + Transform::Normal, Fourcc::Abgr8888, elements, ) { |
