Files
niri/src/render_helpers/mod.rs
T

322 lines
9.5 KiB
Rust
Raw Normal View History

2024-03-08 13:10:55 +01:00
use std::ptr;
use anyhow::{ensure, Context};
2024-05-04 11:51:27 +04:00
use niri_config::BlockOutFrom;
2024-02-06 11:34:36 +04:00
use smithay::backend::allocator::Fourcc;
2024-04-12 20:38:51 +04:00
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
use smithay::backend::renderer::element::{Kind, RenderElement};
2024-02-06 11:34:36 +04:00
use smithay::backend::renderer::gles::{GlesMapping, GlesRenderer, GlesTexture};
use smithay::backend::renderer::sync::SyncPoint;
2024-03-08 13:10:55 +01:00
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::{Logical, Physical, Point, Rectangle, Scale, Size, Transform};
2024-03-08 13:10:55 +01:00
use smithay::wayland::shm;
2024-06-17 09:16:28 +03:00
use solid_color::{SolidColorBuffer, SolidColorRenderElement};
2024-02-06 11:34:36 +04:00
use self::primary_gpu_texture::PrimaryGpuTextureRenderElement;
use self::texture::{TextureBuffer, TextureRenderElement};
2024-05-01 19:06:08 +04:00
pub mod border;
pub mod clipped_surface;
2024-05-02 08:14:21 +04:00
pub mod damage;
2024-05-02 17:52:06 +04:00
pub mod debug;
2024-06-09 10:24:09 +03:00
pub mod memory;
2024-02-07 11:30:33 +04:00
pub mod offscreen;
2024-02-06 11:24:50 +04:00
pub mod primary_gpu_texture;
pub mod render_elements;
pub mod renderer;
2024-04-21 20:10:35 +04:00
pub mod resize;
2024-04-13 11:07:23 +04:00
pub mod resources;
2024-05-01 19:06:08 +04:00
pub mod shader_element;
2024-02-21 21:27:44 +04:00
pub mod shaders;
2024-04-13 14:16:07 +04:00
pub mod snapshot;
2024-06-09 11:56:51 +03:00
pub mod solid_color;
2024-04-09 22:37:10 +04:00
pub mod surface;
pub mod texture;
2024-02-06 11:34:36 +04:00
/// What we're rendering for.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RenderTarget {
/// Rendering to display on screen.
Output,
/// Rendering for a screencast.
Screencast,
/// Rendering for any other screen capture.
ScreenCapture,
}
/// Buffer with location, src and dst.
#[derive(Debug)]
pub struct BakedBuffer<B> {
pub buffer: B,
2024-06-17 09:16:28 +03:00
pub location: Point<f64, Logical>,
pub src: Option<Rectangle<f64, Logical>>,
pub dst: Option<Size<i32, Logical>>,
}
/// Render elements split into normal and popup.
#[derive(Debug)]
pub struct SplitElements<E> {
pub normal: Vec<E>,
pub popups: Vec<E>,
}
pub trait ToRenderElement {
type RenderElement;
fn to_render_element(
&self,
2024-06-17 09:16:28 +03:00
location: Point<f64, Logical>,
scale: Scale<f64>,
alpha: f32,
kind: Kind,
) -> Self::RenderElement;
}
2024-05-04 11:51:27 +04:00
impl RenderTarget {
pub fn should_block_out(self, block_out_from: Option<BlockOutFrom>) -> bool {
match block_out_from {
None => false,
Some(BlockOutFrom::Screencast) => self == RenderTarget::Screencast,
Some(BlockOutFrom::ScreenCapture) => self != RenderTarget::Output,
}
}
}
impl<E> Default for SplitElements<E> {
fn default() -> Self {
Self {
normal: Vec::new(),
popups: Vec::new(),
}
}
}
impl<E> IntoIterator for SplitElements<E> {
type Item = E;
type IntoIter = std::iter::Chain<std::vec::IntoIter<E>, std::vec::IntoIter<E>>;
fn into_iter(self) -> Self::IntoIter {
self.popups.into_iter().chain(self.normal)
}
}
impl<E> SplitElements<E> {
pub fn iter(&self) -> std::iter::Chain<std::slice::Iter<E>, std::slice::Iter<E>> {
self.popups.iter().chain(&self.normal)
}
pub fn into_vec(self) -> Vec<E> {
let Self { normal, mut popups } = self;
popups.extend(normal);
popups
}
}
impl ToRenderElement for BakedBuffer<TextureBuffer<GlesTexture>> {
type RenderElement = PrimaryGpuTextureRenderElement;
fn to_render_element(
&self,
2024-06-17 09:16:28 +03:00
location: Point<f64, Logical>,
_scale: Scale<f64>,
alpha: f32,
kind: Kind,
) -> Self::RenderElement {
let elem = TextureRenderElement::from_texture_buffer(
self.buffer.clone(),
2024-06-17 09:16:28 +03:00
location + self.location,
alpha,
self.src,
self.dst.map(|dst| dst.to_f64()),
kind,
);
PrimaryGpuTextureRenderElement(elem)
}
}
impl ToRenderElement for BakedBuffer<SolidColorBuffer> {
type RenderElement = SolidColorRenderElement;
fn to_render_element(
&self,
2024-06-17 09:16:28 +03:00
location: Point<f64, Logical>,
_scale: Scale<f64>,
alpha: f32,
kind: Kind,
) -> Self::RenderElement {
2024-06-17 09:16:28 +03:00
SolidColorRenderElement::from_buffer(&self.buffer, location + self.location, alpha, kind)
}
}
2024-04-12 20:38:51 +04:00
pub fn render_to_encompassing_texture(
renderer: &mut GlesRenderer,
scale: Scale<f64>,
transform: Transform,
fourcc: Fourcc,
elements: &[impl RenderElement<GlesRenderer>],
) -> anyhow::Result<(GlesTexture, SyncPoint, Rectangle<i32, Physical>)> {
let geo = elements
.iter()
.map(|ele| ele.geometry(scale))
.reduce(|a, b| a.merge(b))
.unwrap_or_default();
let elements = elements.iter().rev().map(|ele| {
RelocateRenderElement::from_element(ele, (-geo.loc.x, -geo.loc.y), Relocate::Relative)
});
let (texture, sync_point) =
render_to_texture(renderer, geo.size, scale, transform, fourcc, elements)?;
Ok((texture, sync_point, geo))
}
2024-02-06 11:34:36 +04:00
pub fn render_to_texture(
renderer: &mut GlesRenderer,
size: Size<i32, Physical>,
scale: Scale<f64>,
2024-03-08 13:10:55 +01:00
transform: Transform,
2024-02-06 11:34:36 +04:00
fourcc: Fourcc,
2024-02-06 17:42:32 +04:00
elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,
2024-02-06 11:34:36 +04:00
) -> anyhow::Result<(GlesTexture, SyncPoint)> {
let _span = tracy_client::span!();
let buffer_size = size.to_logical(1).to_buffer(1, Transform::Normal);
let texture: GlesTexture = renderer
.create_buffer(fourcc, buffer_size)
.context("error creating texture")?;
renderer
.bind(texture.clone())
.context("error binding texture")?;
2024-03-08 13:10:55 +01:00
let sync_point = render_elements(renderer, size, scale, transform, elements)?;
2024-02-06 11:34:36 +04:00
Ok((texture, sync_point))
}
pub fn render_and_download(
renderer: &mut GlesRenderer,
size: Size<i32, Physical>,
scale: Scale<f64>,
2024-03-08 13:10:55 +01:00
transform: Transform,
2024-02-06 11:34:36 +04:00
fourcc: Fourcc,
2024-02-06 17:42:32 +04:00
elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,
2024-02-06 11:34:36 +04:00
) -> anyhow::Result<GlesMapping> {
let _span = tracy_client::span!();
2024-04-09 19:06:13 +04:00
let (_, _) = render_to_texture(renderer, size, scale, transform, fourcc, elements)?;
2024-02-06 11:34:36 +04:00
let buffer_size = size.to_logical(1).to_buffer(1, Transform::Normal);
let mapping = renderer
.copy_framebuffer(Rectangle::from_loc_and_size((0, 0), buffer_size), fourcc)
.context("error copying framebuffer")?;
Ok(mapping)
}
pub fn render_to_vec(
renderer: &mut GlesRenderer,
size: Size<i32, Physical>,
scale: Scale<f64>,
2024-03-08 13:10:55 +01:00
transform: Transform,
2024-02-06 11:34:36 +04:00
fourcc: Fourcc,
2024-02-06 17:42:32 +04:00
elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,
2024-02-06 11:34:36 +04:00
) -> anyhow::Result<Vec<u8>> {
let _span = tracy_client::span!();
2024-03-08 13:10:55 +01:00
let mapping = render_and_download(renderer, size, scale, transform, fourcc, elements)
.context("error rendering")?;
2024-02-06 11:34:36 +04:00
let copy = renderer
.map_texture(&mapping)
.context("error mapping texture")?;
Ok(copy.to_vec())
}
#[cfg(feature = "xdp-gnome-screencast")]
pub fn render_to_dmabuf(
renderer: &mut GlesRenderer,
dmabuf: smithay::backend::allocator::dmabuf::Dmabuf,
size: Size<i32, Physical>,
scale: Scale<f64>,
2024-03-08 13:10:55 +01:00
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,
2024-02-06 17:42:32 +04:00
elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,
2024-02-06 11:34:36 +04:00
) -> anyhow::Result<()> {
let _span = tracy_client::span!();
2024-03-08 13:10:55 +01:00
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));
2024-02-06 11:34:36 +04:00
let mut frame = renderer
2024-03-08 13:10:55 +01:00
.render(size, transform)
2024-02-06 11:34:36 +04:00
.context("error starting frame")?;
2024-02-07 11:18:55 +04:00
frame
.clear([0., 0., 0., 0.], &[output_rect])
.context("error clearing")?;
2024-02-06 17:42:32 +04:00
for element in elements {
2024-02-06 11:34:36 +04:00
let src = element.src();
let dst = element.geometry(scale);
if let Some(mut damage) = output_rect.intersection(dst) {
damage.loc -= dst.loc;
element
2024-06-12 09:40:19 +03:00
.draw(&mut frame, src, dst, &[damage], &[])
2024-02-06 11:34:36 +04:00
.context("error drawing element")?;
}
}
2024-03-08 13:10:55 +01:00
frame.finish().context("error finishing frame")
2024-02-06 11:34:36 +04:00
}