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};
|
2024-04-10 08:53:35 +04:00
|
|
|
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;
|
2024-04-10 08:53:35 +04:00
|
|
|
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
|
|
|
|
2024-04-10 08:53:35 +04:00
|
|
|
use self::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
2024-06-01 12:27:30 +03:00
|
|
|
use self::texture::{TextureBuffer, TextureRenderElement};
|
2024-04-10 08:53:35 +04:00
|
|
|
|
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;
|
2024-06-01 12:27:30 +03:00
|
|
|
pub mod texture;
|
2024-02-06 11:34:36 +04:00
|
|
|
|
2024-03-24 09:03:59 +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,
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-10 08:53:35 +04:00
|
|
|
/// 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>,
|
2024-04-10 08:53:35 +04:00
|
|
|
pub src: Option<Rectangle<f64, Logical>>,
|
|
|
|
|
pub dst: Option<Size<i32, Logical>>,
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 19:02:22 +04:00
|
|
|
/// Render elements split into normal and popup.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct SplitElements<E> {
|
|
|
|
|
pub normal: Vec<E>,
|
|
|
|
|
pub popups: Vec<E>,
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-10 08:53:35 +04:00
|
|
|
pub trait ToRenderElement {
|
|
|
|
|
type RenderElement;
|
|
|
|
|
|
|
|
|
|
fn to_render_element(
|
|
|
|
|
&self,
|
2024-06-17 09:16:28 +03:00
|
|
|
location: Point<f64, Logical>,
|
2024-04-10 08:53:35 +04:00
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 19:02:22 +04:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-10 08:53:35 +04:00
|
|
|
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>,
|
2024-06-01 12:27:30 +03:00
|
|
|
_scale: Scale<f64>,
|
2024-04-10 08:53:35 +04:00
|
|
|
alpha: f32,
|
|
|
|
|
kind: Kind,
|
|
|
|
|
) -> Self::RenderElement {
|
|
|
|
|
let elem = TextureRenderElement::from_texture_buffer(
|
2024-06-01 12:27:30 +03:00
|
|
|
self.buffer.clone(),
|
2024-06-17 09:16:28 +03:00
|
|
|
location + self.location,
|
2024-06-01 12:27:30 +03:00
|
|
|
alpha,
|
2024-04-10 08:53:35 +04:00
|
|
|
self.src,
|
2024-06-01 12:27:30 +03:00
|
|
|
self.dst.map(|dst| dst.to_f64()),
|
2024-04-10 08:53:35 +04:00
|
|
|
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>,
|
2024-04-10 08:53:35 +04:00
|
|
|
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-10 08:53:35 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
}
|