aboutsummaryrefslogtreecommitdiff
path: root/src/render_helpers
diff options
context:
space:
mode:
authorsodiboo <37938646+sodiboo@users.noreply.github.com>2024-03-08 13:10:55 +0100
committerGitHub <noreply@github.com>2024-03-08 04:10:55 -0800
commitca22e70cc4a868fdb4dec2790ec71fb9a2cfb6bd (patch)
tree69373c13068005180cc75372e16cf493dcb98e38 /src/render_helpers
parent1a784e6e66785f360da6df59ae2fb4b98370ca3c (diff)
downloadniri-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.rs102
-rw-r--r--src/render_helpers/offscreen.rs1
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,
) {