Split rendering between popups and window surface

This commit is contained in:
Ivan Molodetskikh 2024-05-01 19:02:22 +04:00
parent c5be2dd549
commit 9309b3be61
5 changed files with 218 additions and 36 deletions

View File

@ -4,7 +4,7 @@ use std::rc::Rc;
use niri::layout::{LayoutElement, LayoutElementRenderElement, LayoutElementRenderSnapshot};
use niri::render_helpers::renderer::NiriRenderer;
use niri::render_helpers::RenderTarget;
use niri::render_helpers::{RenderTarget, SplitElements};
use niri::window::ResolvedWindowRules;
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::{Id, Kind};
@ -149,28 +149,31 @@ impl LayoutElement for TestWindow {
scale: Scale<f64>,
alpha: f32,
_target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>> {
) -> SplitElements<LayoutElementRenderElement<R>> {
let inner = self.inner.borrow();
vec![
SolidColorRenderElement::from_buffer(
&inner.buffer,
location.to_physical_precise_round(scale),
scale,
alpha,
Kind::Unspecified,
)
.into(),
SolidColorRenderElement::from_buffer(
&inner.csd_shadow_buffer,
(location - Point::from((inner.csd_shadow_width, inner.csd_shadow_width)))
.to_physical_precise_round(scale),
scale,
alpha,
Kind::Unspecified,
)
.into(),
]
SplitElements {
normal: vec![
SolidColorRenderElement::from_buffer(
&inner.buffer,
location.to_physical_precise_round(scale),
scale,
alpha,
Kind::Unspecified,
)
.into(),
SolidColorRenderElement::from_buffer(
&inner.csd_shadow_buffer,
(location - Point::from((inner.csd_shadow_width, inner.csd_shadow_width)))
.to_physical_precise_round(scale),
scale,
alpha,
Kind::Unspecified,
)
.into(),
],
popups: vec![],
}
}
fn request_size(&mut self, size: Size<i32, Logical>, _animate: bool) {

View File

@ -51,7 +51,7 @@ use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Works
use crate::niri_render_elements;
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::snapshot::RenderSnapshot;
use crate::render_helpers::{BakedBuffer, RenderTarget};
use crate::render_helpers::{BakedBuffer, RenderTarget, SplitElements};
use crate::utils::output_size;
use crate::window::ResolvedWindowRules;
@ -108,7 +108,31 @@ pub trait LayoutElement {
scale: Scale<f64>,
alpha: f32,
target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>>;
) -> SplitElements<LayoutElementRenderElement<R>>;
/// Renders the non-popup parts of the element.
fn render_normal<R: NiriRenderer>(
&self,
renderer: &mut R,
location: Point<i32, Logical>,
scale: Scale<f64>,
alpha: f32,
target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>> {
self.render(renderer, location, scale, alpha, target).normal
}
/// Renders the popups of the element.
fn render_popups<R: NiriRenderer>(
&self,
renderer: &mut R,
location: Point<i32, Logical>,
scale: Scale<f64>,
alpha: f32,
target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>> {
self.render(renderer, location, scale, alpha, target).popups
}
fn request_size(&mut self, size: Size<i32, Logical>, animate: bool);
fn request_fullscreen(&self, size: Size<i32, Logical>);
@ -1984,8 +2008,8 @@ mod tests {
_scale: Scale<f64>,
_alpha: f32,
_target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>> {
vec![]
) -> SplitElements<LayoutElementRenderElement<R>> {
SplitElements::default()
}
fn request_size(&mut self, size: Size<i32, Logical>, _animate: bool) {

View File

@ -536,18 +536,31 @@ impl<W: LayoutElement> Tile<W> {
let window_render_loc = location + window_loc;
let area = Rectangle::from_loc_and_size(window_render_loc, animated_window_size);
let gles_renderer = renderer.as_gles_renderer();
// If we're resizing, try to render a shader, or a fallback.
let mut resize_shader = None;
let mut resize_popups = None;
let mut resize_fallback = None;
if let Some(resize) = &self.resize_animation {
if let Some(shader) = ResizeRenderElement::shader(gles_renderer) {
resize_popups = Some(
self.window
.render_popups(renderer, window_render_loc, scale, alpha, target)
.into_iter()
.map(Into::into),
);
if let Some(shader) = ResizeRenderElement::shader(renderer) {
let gles_renderer = renderer.as_gles_renderer();
if let Some(texture_from) = resize.snapshot.texture(gles_renderer, scale, target) {
let window_elements =
self.window
.render(gles_renderer, Point::from((0, 0)), scale, 1., target);
let window_elements = self.window.render_normal(
gles_renderer,
Point::from((0, 0)),
scale,
1.,
target,
);
let current = render_to_encompassing_texture(
gles_renderer,
scale,
@ -605,8 +618,10 @@ impl<W: LayoutElement> Tile<W> {
);
}
let rv = resize_shader
let rv = resize_popups
.into_iter()
.flatten()
.chain(resize_shader)
.chain(resize_fallback)
.chain(window.into_iter().flatten());

View File

@ -49,6 +49,13 @@ pub struct BakedBuffer<B> {
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;
@ -61,6 +68,36 @@ pub trait ToRenderElement {
) -> Self::RenderElement;
}
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;

View File

@ -3,7 +3,8 @@ use std::cmp::{max, min};
use niri_config::{BlockOutFrom, WindowRule};
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::{AsRenderElements, Id, Kind};
use smithay::backend::renderer::element::surface::render_elements_from_surface_tree;
use smithay::backend::renderer::element::{Id, Kind};
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::desktop::space::SpaceElement as _;
use smithay::desktop::{PopupManager, Window};
@ -23,7 +24,7 @@ use crate::niri::WindowOffscreenId;
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::snapshot::RenderSnapshot;
use crate::render_helpers::surface::render_snapshot_from_surface_tree;
use crate::render_helpers::{BakedBuffer, RenderTarget};
use crate::render_helpers::{BakedBuffer, RenderTarget, SplitElements};
#[derive(Debug)]
pub struct Mapped {
@ -203,6 +204,63 @@ impl LayoutElement for Mapped {
scale: Scale<f64>,
alpha: f32,
target: RenderTarget,
) -> SplitElements<LayoutElementRenderElement<R>> {
let mut rv = SplitElements::default();
let block_out = match self.rules.block_out_from {
None => false,
Some(BlockOutFrom::Screencast) => target == RenderTarget::Screencast,
Some(BlockOutFrom::ScreenCapture) => target != RenderTarget::Output,
};
if block_out {
let mut buffer = self.block_out_buffer.borrow_mut();
buffer.resize(self.window.geometry().size);
let elem = SolidColorRenderElement::from_buffer(
&buffer,
location.to_physical_precise_round(scale),
scale,
alpha,
Kind::Unspecified,
);
rv.normal.push(elem.into());
} else {
let buf_pos = location - self.window.geometry().loc;
let surface = self.toplevel().wl_surface();
for (popup, popup_offset) in PopupManager::popups_for_surface(surface) {
let offset = self.window.geometry().loc + popup_offset - popup.geometry().loc;
rv.popups.extend(render_elements_from_surface_tree(
renderer,
popup.wl_surface(),
(buf_pos + offset).to_physical_precise_round(scale),
scale,
alpha,
Kind::Unspecified,
));
}
rv.normal = render_elements_from_surface_tree(
renderer,
surface,
buf_pos.to_physical_precise_round(scale),
scale,
alpha,
Kind::Unspecified,
);
}
rv
}
fn render_normal<R: NiriRenderer>(
&self,
renderer: &mut R,
location: Point<i32, Logical>,
scale: Scale<f64>,
alpha: f32,
target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>> {
let block_out = match self.rules.block_out_from {
None => false,
@ -223,8 +281,53 @@ impl LayoutElement for Mapped {
vec![elem.into()]
} else {
let buf_pos = location - self.window.geometry().loc;
let buf_pos = buf_pos.to_physical_precise_round(scale);
self.window.render_elements(renderer, buf_pos, scale, alpha)
let surface = self.toplevel().wl_surface();
render_elements_from_surface_tree(
renderer,
surface,
buf_pos.to_physical_precise_round(scale),
scale,
alpha,
Kind::Unspecified,
)
}
}
fn render_popups<R: NiriRenderer>(
&self,
renderer: &mut R,
location: Point<i32, Logical>,
scale: Scale<f64>,
alpha: f32,
target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>> {
let block_out = match self.rules.block_out_from {
None => false,
Some(BlockOutFrom::Screencast) => target == RenderTarget::Screencast,
Some(BlockOutFrom::ScreenCapture) => target != RenderTarget::Output,
};
if block_out {
vec![]
} else {
let mut rv = vec![];
let buf_pos = location - self.window.geometry().loc;
let surface = self.toplevel().wl_surface();
for (popup, popup_offset) in PopupManager::popups_for_surface(surface) {
let offset = self.window.geometry().loc + popup_offset - popup.geometry().loc;
rv.extend(render_elements_from_surface_tree(
renderer,
popup.wl_surface(),
(buf_pos + offset).to_physical_precise_round(scale),
scale,
alpha,
Kind::Unspecified,
));
}
rv
}
}