mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
Reimplement window closing anim in an efficient way
- Keep a root surface cache to be accessible in surface destroyed() - Only snapshot during / right before closing, rather than every frame - Store textures rather than elements to handle scale and alpha properly
This commit is contained in:
@@ -2,9 +2,7 @@ use std::cell::RefCell;
|
||||
use std::cmp::{max, min};
|
||||
use std::rc::Rc;
|
||||
|
||||
use niri::layout::{
|
||||
LayoutElement, LayoutElementRenderElement, LayoutElementSnapshotRenderElements,
|
||||
};
|
||||
use niri::layout::{LayoutElement, LayoutElementRenderElement, LayoutElementRenderSnapshot};
|
||||
use niri::render_helpers::renderer::NiriRenderer;
|
||||
use niri::render_helpers::{RenderSnapshot, RenderTarget};
|
||||
use niri::window::ResolvedWindowRules;
|
||||
@@ -175,7 +173,7 @@ impl LayoutElement for TestWindow {
|
||||
]
|
||||
}
|
||||
|
||||
fn take_last_render(&self) -> RenderSnapshot<LayoutElementSnapshotRenderElements> {
|
||||
fn take_last_render(&self) -> LayoutElementRenderSnapshot {
|
||||
RenderSnapshot::default()
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ use smithay::wayland::dmabuf::get_dmabuf;
|
||||
use smithay::wayland::shm::{ShmHandler, ShmState};
|
||||
use smithay::{delegate_compositor, delegate_shm};
|
||||
|
||||
use crate::layout::LayoutElement;
|
||||
use crate::niri::{ClientState, State};
|
||||
use crate::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped};
|
||||
|
||||
@@ -80,6 +81,33 @@ impl CompositorHandler for State {
|
||||
fn commit(&mut self, surface: &WlSurface) {
|
||||
let _span = tracy_client::span!("CompositorHandler::commit");
|
||||
|
||||
let mut root_surface = surface.clone();
|
||||
while let Some(parent) = get_parent(&root_surface) {
|
||||
root_surface = parent;
|
||||
}
|
||||
|
||||
// Update the cached root surface.
|
||||
self.niri
|
||||
.root_surface
|
||||
.insert(surface.clone(), root_surface.clone());
|
||||
|
||||
// Check if this root surface got unmapped to snapshot it before on_commit_buffer_handler()
|
||||
// overwrites the buffer.
|
||||
if surface == &root_surface {
|
||||
let got_unmapped = with_states(surface, |states| {
|
||||
let attrs = states.cached_state.current::<SurfaceAttributes>();
|
||||
matches!(attrs.buffer, Some(BufferAssignment::Removed))
|
||||
});
|
||||
|
||||
if got_unmapped {
|
||||
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(surface) {
|
||||
self.backend.with_primary_renderer(|renderer| {
|
||||
mapped.render_and_store_snapshot(renderer);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
on_commit_buffer_handler::<Self>(surface);
|
||||
self.backend.early_import(surface);
|
||||
|
||||
@@ -87,11 +115,6 @@ impl CompositorHandler for State {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut root_surface = surface.clone();
|
||||
while let Some(parent) = get_parent(&root_surface) {
|
||||
root_surface = parent;
|
||||
}
|
||||
|
||||
if surface == &root_surface {
|
||||
// This is a root surface commit. It might have mapped a previously-unmapped toplevel.
|
||||
if let Entry::Occupied(entry) = self.niri.unmapped_windows.entry(surface.clone()) {
|
||||
@@ -195,8 +218,11 @@ impl CompositorHandler for State {
|
||||
false
|
||||
});
|
||||
|
||||
// Must start the close animation before window.on_commit().
|
||||
if !is_mapped {
|
||||
if is_mapped {
|
||||
// The surface remains mapped; clear any cached render snapshot.
|
||||
let _ = mapped.take_last_render();
|
||||
} else {
|
||||
// Must start the close animation before window.on_commit().
|
||||
self.backend.with_primary_renderer(|renderer| {
|
||||
self.niri
|
||||
.layout
|
||||
@@ -288,6 +314,24 @@ impl CompositorHandler for State {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(&mut self, surface: &WlSurface) {
|
||||
// Clients may destroy their subsurfaces before the main surface. Ensure we have a snapshot
|
||||
// when that happens, so that the closing animation includes all these subsurfaces.
|
||||
//
|
||||
// Test client: alacritty with CSD.
|
||||
if let Some(root) = self.niri.root_surface.get(surface) {
|
||||
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(root) {
|
||||
self.backend.with_primary_renderer(|renderer| {
|
||||
mapped.render_and_store_snapshot(renderer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.niri
|
||||
.root_surface
|
||||
.retain(|k, v| k != surface && v != surface);
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferHandler for State {
|
||||
|
||||
@@ -382,6 +382,9 @@ impl XdgShellHandler for State {
|
||||
let window = mapped.window.clone();
|
||||
let output = output.clone();
|
||||
|
||||
self.backend.with_primary_renderer(|renderer| {
|
||||
mapped.render_and_store_snapshot(renderer);
|
||||
});
|
||||
self.backend.with_primary_renderer(|renderer| {
|
||||
self.niri
|
||||
.layout
|
||||
|
||||
@@ -59,7 +59,7 @@ impl ClosingWindow {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new<E: RenderElement<GlesRenderer>>(
|
||||
renderer: &mut GlesRenderer,
|
||||
snapshot: RenderSnapshot<E>,
|
||||
snapshot: RenderSnapshot<E, E>,
|
||||
scale: i32,
|
||||
center: Point<i32, Logical>,
|
||||
pos: Point<i32, Logical>,
|
||||
|
||||
+8
-12
@@ -36,10 +36,11 @@ use std::time::Duration;
|
||||
|
||||
use niri_config::{CenterFocusedColumn, Config, Struts};
|
||||
use niri_ipc::SizeChange;
|
||||
use smithay::backend::renderer::element::solid::SolidColorRenderElement;
|
||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
|
||||
use smithay::backend::renderer::element::texture::TextureBuffer;
|
||||
use smithay::backend::renderer::element::Id;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::utils::{Logical, Point, Scale, Size, Transform};
|
||||
@@ -48,9 +49,8 @@ use self::monitor::Monitor;
|
||||
pub use self::monitor::MonitorRenderElement;
|
||||
use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Workspace};
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
use crate::render_helpers::{RenderSnapshot, RenderTarget};
|
||||
use crate::render_helpers::{BakedBuffer, RenderSnapshot, RenderTarget};
|
||||
use crate::utils::output_size;
|
||||
use crate::window::ResolvedWindowRules;
|
||||
|
||||
@@ -67,12 +67,8 @@ niri_render_elements! {
|
||||
}
|
||||
}
|
||||
|
||||
niri_render_elements! {
|
||||
LayoutElementSnapshotRenderElements => {
|
||||
Texture = PrimaryGpuTextureRenderElement,
|
||||
SolidColor = SolidColorRenderElement,
|
||||
}
|
||||
}
|
||||
pub type LayoutElementRenderSnapshot =
|
||||
RenderSnapshot<BakedBuffer<TextureBuffer<GlesTexture>>, BakedBuffer<SolidColorBuffer>>;
|
||||
|
||||
pub trait LayoutElement {
|
||||
/// Type that can be used as a unique ID of this element.
|
||||
@@ -110,7 +106,7 @@ pub trait LayoutElement {
|
||||
target: RenderTarget,
|
||||
) -> Vec<LayoutElementRenderElement<R>>;
|
||||
|
||||
fn take_last_render(&self) -> RenderSnapshot<LayoutElementSnapshotRenderElements>;
|
||||
fn take_last_render(&self) -> LayoutElementRenderSnapshot;
|
||||
|
||||
fn request_size(&self, size: Size<i32, Logical>);
|
||||
fn request_fullscreen(&self, size: Size<i32, Logical>);
|
||||
@@ -1930,7 +1926,7 @@ mod tests {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn take_last_render(&self) -> RenderSnapshot<LayoutElementSnapshotRenderElements> {
|
||||
fn take_last_render(&self) -> LayoutElementRenderSnapshot {
|
||||
RenderSnapshot::default()
|
||||
}
|
||||
|
||||
|
||||
+58
-43
@@ -3,22 +3,19 @@ use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use smithay::backend::renderer::element::utils::{
|
||||
Relocate, RelocateRenderElement, RescaleRenderElement,
|
||||
};
|
||||
use smithay::backend::renderer::element::utils::RescaleRenderElement;
|
||||
use smithay::backend::renderer::element::{Element, Kind};
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Point, Rectangle, Scale, Size};
|
||||
|
||||
use super::focus_ring::{FocusRing, FocusRingRenderElement};
|
||||
use super::{
|
||||
LayoutElement, LayoutElementRenderElement, LayoutElementSnapshotRenderElements, Options,
|
||||
};
|
||||
use super::{LayoutElement, LayoutElementRenderElement, Options};
|
||||
use crate::animation::Animation;
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::offscreen::OffscreenRenderElement;
|
||||
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
use crate::render_helpers::{RenderSnapshot, RenderTarget};
|
||||
use crate::render_helpers::{RenderSnapshot, RenderTarget, ToRenderElement};
|
||||
|
||||
/// Toplevel window with decorations.
|
||||
#[derive(Debug)]
|
||||
@@ -65,7 +62,7 @@ niri_render_elements! {
|
||||
|
||||
niri_render_elements! {
|
||||
TileSnapshotRenderElement => {
|
||||
LayoutElement = RelocateRenderElement<LayoutElementSnapshotRenderElements>,
|
||||
Texture = PrimaryGpuTextureRenderElement,
|
||||
FocusRing = FocusRingRenderElement,
|
||||
SolidColor = SolidColorRenderElement,
|
||||
}
|
||||
@@ -417,52 +414,70 @@ impl<W: LayoutElement> Tile<W> {
|
||||
}
|
||||
}
|
||||
|
||||
fn render_snapshot<C>(
|
||||
&self,
|
||||
renderer: &mut GlesRenderer,
|
||||
scale: Scale<f64>,
|
||||
view_size: Size<i32, Logical>,
|
||||
contents: Vec<C>,
|
||||
) -> Vec<TileSnapshotRenderElement>
|
||||
where
|
||||
C: ToRenderElement<RenderElement: Into<TileSnapshotRenderElement>>,
|
||||
{
|
||||
let alpha = if self.is_fullscreen {
|
||||
1.
|
||||
} else {
|
||||
self.window.rules().opacity.unwrap_or(1.).clamp(0., 1.)
|
||||
};
|
||||
|
||||
let mut rv = vec![];
|
||||
|
||||
for baked in contents {
|
||||
let elem = baked.to_render_element(self.window_loc(), scale, alpha, Kind::Unspecified);
|
||||
rv.push(elem.into());
|
||||
}
|
||||
|
||||
if let Some(width) = self.effective_border_width() {
|
||||
rv.extend(
|
||||
self.border
|
||||
.render(renderer, Point::from((width, width)), scale, view_size)
|
||||
.map(Into::into),
|
||||
);
|
||||
}
|
||||
|
||||
if self.is_fullscreen {
|
||||
let elem = SolidColorRenderElement::from_buffer(
|
||||
&self.fullscreen_backdrop,
|
||||
Point::from((0, 0)),
|
||||
scale,
|
||||
1.,
|
||||
Kind::Unspecified,
|
||||
);
|
||||
rv.push(elem.into());
|
||||
}
|
||||
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn take_snapshot_for_close_anim(
|
||||
&self,
|
||||
renderer: &mut GlesRenderer,
|
||||
scale: Scale<f64>,
|
||||
view_size: Size<i32, Logical>,
|
||||
) -> RenderSnapshot<TileSnapshotRenderElement> {
|
||||
) -> RenderSnapshot<TileSnapshotRenderElement, TileSnapshotRenderElement> {
|
||||
let snapshot = self.window.take_last_render();
|
||||
if snapshot.contents.is_empty() {
|
||||
return RenderSnapshot::default();
|
||||
}
|
||||
|
||||
let mut process = |contents| {
|
||||
let mut rv = vec![];
|
||||
|
||||
let buf_pos =
|
||||
(self.window_loc() + self.window.buf_loc()).to_physical_precise_round(scale);
|
||||
for elem in contents {
|
||||
let elem = RelocateRenderElement::from_element(elem, buf_pos, Relocate::Relative);
|
||||
rv.push(elem.into());
|
||||
}
|
||||
|
||||
if let Some(width) = self.effective_border_width() {
|
||||
rv.extend(
|
||||
self.border
|
||||
.render(renderer, Point::from((width, width)), scale, view_size)
|
||||
.map(Into::into),
|
||||
);
|
||||
}
|
||||
|
||||
if self.is_fullscreen {
|
||||
let elem = SolidColorRenderElement::from_buffer(
|
||||
&self.fullscreen_backdrop,
|
||||
Point::from((0, 0)),
|
||||
scale,
|
||||
1.,
|
||||
Kind::Unspecified,
|
||||
);
|
||||
rv.push(elem.into());
|
||||
}
|
||||
|
||||
rv
|
||||
};
|
||||
|
||||
RenderSnapshot {
|
||||
contents: process(snapshot.contents),
|
||||
blocked_out_contents: process(snapshot.blocked_out_contents),
|
||||
contents: self.render_snapshot(renderer, scale, view_size, snapshot.contents),
|
||||
blocked_out_contents: self.render_snapshot(
|
||||
renderer,
|
||||
scale,
|
||||
view_size,
|
||||
snapshot.blocked_out_contents,
|
||||
),
|
||||
block_out_from: snapshot.block_out_from,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,10 @@ pub struct Niri {
|
||||
// Windows which don't have a buffer attached yet.
|
||||
pub unmapped_windows: HashMap<WlSurface, Unmapped>,
|
||||
|
||||
// Cached root surface for every surface, so that we can access it in destroyed() where the
|
||||
// normal get_parent() is cleared out.
|
||||
pub root_surface: HashMap<WlSurface, WlSurface>,
|
||||
|
||||
pub output_state: HashMap<Output, OutputState>,
|
||||
pub output_by_name: HashMap<String, Output>,
|
||||
|
||||
@@ -1318,6 +1322,7 @@ impl Niri {
|
||||
output_state: HashMap::new(),
|
||||
output_by_name: HashMap::new(),
|
||||
unmapped_windows: HashMap::new(),
|
||||
root_surface: HashMap::new(),
|
||||
monitors_active: true,
|
||||
|
||||
devices: HashSet::new(),
|
||||
|
||||
@@ -3,15 +3,19 @@ use std::ptr;
|
||||
use anyhow::{ensure, Context};
|
||||
use niri_config::BlockOutFrom;
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use smithay::backend::renderer::element::texture::{TextureBuffer, TextureRenderElement};
|
||||
use smithay::backend::renderer::element::{Kind, RenderElement};
|
||||
use smithay::backend::renderer::gles::{GlesMapping, GlesRenderer, GlesTexture};
|
||||
use smithay::backend::renderer::sync::SyncPoint;
|
||||
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::utils::{Logical, Physical, Point, Rectangle, Scale, Size, Transform};
|
||||
use smithay::wayland::shm;
|
||||
|
||||
use self::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||
|
||||
pub mod gradient;
|
||||
pub mod offscreen;
|
||||
pub mod primary_gpu_pixel_shader;
|
||||
@@ -32,20 +36,85 @@ pub enum RenderTarget {
|
||||
ScreenCapture,
|
||||
}
|
||||
|
||||
/// Buffer with location, src and dst.
|
||||
#[derive(Debug)]
|
||||
pub struct BakedBuffer<B> {
|
||||
pub buffer: B,
|
||||
pub location: Point<i32, Logical>,
|
||||
pub src: Option<Rectangle<f64, Logical>>,
|
||||
pub dst: Option<Size<i32, Logical>>,
|
||||
}
|
||||
|
||||
/// Snapshot of a render.
|
||||
#[derive(Debug)]
|
||||
pub struct RenderSnapshot<E> {
|
||||
pub struct RenderSnapshot<C, B> {
|
||||
/// Contents for a normal render.
|
||||
pub contents: Vec<E>,
|
||||
pub contents: Vec<C>,
|
||||
|
||||
/// Blocked-out contents.
|
||||
pub blocked_out_contents: Vec<E>,
|
||||
pub blocked_out_contents: Vec<B>,
|
||||
|
||||
/// Where the contents were blocked out from at the time of the snapshot.
|
||||
pub block_out_from: Option<BlockOutFrom>,
|
||||
}
|
||||
|
||||
impl<E> Default for RenderSnapshot<E> {
|
||||
pub trait ToRenderElement {
|
||||
type RenderElement;
|
||||
|
||||
fn to_render_element(
|
||||
&self,
|
||||
location: Point<i32, Logical>,
|
||||
scale: Scale<f64>,
|
||||
alpha: f32,
|
||||
kind: Kind,
|
||||
) -> Self::RenderElement;
|
||||
}
|
||||
|
||||
impl ToRenderElement for BakedBuffer<TextureBuffer<GlesTexture>> {
|
||||
type RenderElement = PrimaryGpuTextureRenderElement;
|
||||
|
||||
fn to_render_element(
|
||||
&self,
|
||||
location: Point<i32, Logical>,
|
||||
scale: Scale<f64>,
|
||||
alpha: f32,
|
||||
kind: Kind,
|
||||
) -> Self::RenderElement {
|
||||
let elem = TextureRenderElement::from_texture_buffer(
|
||||
(location + self.location).to_physical_precise_round(scale),
|
||||
&self.buffer,
|
||||
Some(alpha),
|
||||
self.src,
|
||||
self.dst,
|
||||
kind,
|
||||
);
|
||||
PrimaryGpuTextureRenderElement(elem)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToRenderElement for BakedBuffer<SolidColorBuffer> {
|
||||
type RenderElement = SolidColorRenderElement;
|
||||
|
||||
fn to_render_element(
|
||||
&self,
|
||||
location: Point<i32, Logical>,
|
||||
scale: Scale<f64>,
|
||||
alpha: f32,
|
||||
kind: Kind,
|
||||
) -> Self::RenderElement {
|
||||
SolidColorRenderElement::from_buffer(
|
||||
&self.buffer,
|
||||
(location + self.location)
|
||||
.to_physical_precise_round(scale)
|
||||
.to_i32_round(),
|
||||
scale,
|
||||
alpha,
|
||||
kind,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, B> Default for RenderSnapshot<C, B> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
contents: Default::default(),
|
||||
|
||||
@@ -1,41 +1,25 @@
|
||||
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
|
||||
use smithay::backend::renderer::element::texture::{TextureBuffer, TextureRenderElement};
|
||||
use smithay::backend::renderer::element::Kind;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::backend::renderer::element::texture::TextureBuffer;
|
||||
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
|
||||
use smithay::backend::renderer::utils::{import_surface, RendererSurfaceStateUserData};
|
||||
use smithay::backend::renderer::Renderer as _;
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::utils::{Physical, Point, Scale};
|
||||
use smithay::utils::{Logical, Point};
|
||||
use smithay::wayland::compositor::{with_surface_tree_downward, TraversalAction};
|
||||
|
||||
use super::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||
use super::renderer::NiriRenderer;
|
||||
use crate::layout::{LayoutElementRenderElement, LayoutElementSnapshotRenderElements};
|
||||
use super::BakedBuffer;
|
||||
|
||||
/// Renders elements from a surface tree, as well as saves them as textures into `storage`.
|
||||
///
|
||||
/// Saved textures are based at (0, 0) to facilitate later offscreening. This is why the location
|
||||
/// argument is split into `location` and `offset`: the former is ignored for saved textures, but
|
||||
/// the latter isn't (for things like popups).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn render_and_save_from_surface_tree<R: NiriRenderer>(
|
||||
renderer: &mut R,
|
||||
/// Renders elements from a surface tree as textures into `storage`.
|
||||
pub fn render_snapshot_from_surface_tree(
|
||||
renderer: &mut GlesRenderer,
|
||||
surface: &WlSurface,
|
||||
location: Point<f64, Physical>,
|
||||
offset: Point<f64, Physical>,
|
||||
scale: Scale<f64>,
|
||||
alpha: f32,
|
||||
kind: Kind,
|
||||
elements: &mut Vec<LayoutElementRenderElement<R>>,
|
||||
storage: &mut Option<&mut Vec<LayoutElementSnapshotRenderElements>>,
|
||||
location: Point<i32, Logical>,
|
||||
storage: &mut Vec<BakedBuffer<TextureBuffer<GlesTexture>>>,
|
||||
) {
|
||||
let _span = tracy_client::span!("render_and_save_from_surface_tree");
|
||||
|
||||
let base_pos = location;
|
||||
let _span = tracy_client::span!("render_snapshot_from_surface_tree");
|
||||
|
||||
with_surface_tree_downward(
|
||||
surface,
|
||||
location + offset,
|
||||
location,
|
||||
|_, states, location| {
|
||||
let mut location = *location;
|
||||
let data = states.data_map.get::<RendererSurfaceStateUserData>();
|
||||
@@ -44,7 +28,7 @@ pub fn render_and_save_from_surface_tree<R: NiriRenderer>(
|
||||
let data = &*data.borrow();
|
||||
|
||||
if let Some(view) = data.view() {
|
||||
location += view.offset.to_f64().to_physical(scale);
|
||||
location += view.offset;
|
||||
TraversalAction::DoChildren(location)
|
||||
} else {
|
||||
TraversalAction::SkipChildren
|
||||
@@ -53,62 +37,44 @@ pub fn render_and_save_from_surface_tree<R: NiriRenderer>(
|
||||
TraversalAction::SkipChildren
|
||||
}
|
||||
},
|
||||
|surface, states, location| {
|
||||
|_, states, location| {
|
||||
let mut location = *location;
|
||||
let data = states.data_map.get::<RendererSurfaceStateUserData>();
|
||||
|
||||
if let Some(data) = data {
|
||||
if let Some(view) = data.borrow().view() {
|
||||
location += view.offset.to_f64().to_physical(scale);
|
||||
location += view.offset;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
let elem = match WaylandSurfaceRenderElement::from_surface(
|
||||
renderer, surface, states, location, alpha, kind,
|
||||
) {
|
||||
Ok(elem) => elem,
|
||||
Err(err) => {
|
||||
warn!("failed to import surface: {err:?}");
|
||||
return;
|
||||
}
|
||||
if let Err(err) = import_surface(renderer, states) {
|
||||
warn!("failed to import surface: {err:?}");
|
||||
return;
|
||||
}
|
||||
|
||||
let data = data.borrow();
|
||||
let view = data.view().unwrap();
|
||||
let Some(texture) = data.texture::<GlesRenderer>(renderer.id()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
elements.push(elem.into());
|
||||
let buffer = TextureBuffer::from_texture(
|
||||
renderer,
|
||||
texture.clone(),
|
||||
data.buffer_scale(),
|
||||
data.buffer_transform(),
|
||||
None,
|
||||
);
|
||||
|
||||
if let Some(storage) = storage {
|
||||
let renderer = renderer.as_gles_renderer();
|
||||
// FIXME (possibly in Smithay): this causes a re-upload for shm textures.
|
||||
if let Err(err) = import_surface(renderer, states) {
|
||||
warn!("failed to import surface: {err:?}");
|
||||
return;
|
||||
}
|
||||
let baked = BakedBuffer {
|
||||
buffer,
|
||||
location,
|
||||
src: Some(view.src),
|
||||
dst: Some(view.dst),
|
||||
};
|
||||
|
||||
let data = data.borrow();
|
||||
let view = data.view().unwrap();
|
||||
let Some(texture) = data.texture::<GlesRenderer>(renderer.id()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let buffer = TextureBuffer::from_texture(
|
||||
renderer,
|
||||
texture.clone(),
|
||||
data.buffer_scale(),
|
||||
data.buffer_transform(),
|
||||
None,
|
||||
);
|
||||
|
||||
let elem = TextureRenderElement::from_texture_buffer(
|
||||
location - base_pos,
|
||||
&buffer,
|
||||
Some(alpha),
|
||||
Some(view.src),
|
||||
Some(view.dst),
|
||||
kind,
|
||||
);
|
||||
|
||||
storage.push(PrimaryGpuTextureRenderElement(elem).into());
|
||||
}
|
||||
storage.push(baked);
|
||||
}
|
||||
},
|
||||
|_, _, _| true,
|
||||
|
||||
+46
-65
@@ -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::{Id, Kind};
|
||||
use smithay::backend::renderer::element::{AsRenderElements, Id, Kind};
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::desktop::space::SpaceElement as _;
|
||||
use smithay::desktop::{PopupManager, Window};
|
||||
use smithay::output::Output;
|
||||
@@ -15,13 +16,11 @@ use smithay::wayland::compositor::{send_surface_state, with_states};
|
||||
use smithay::wayland::shell::xdg::{SurfaceCachedState, ToplevelSurface};
|
||||
|
||||
use super::{ResolvedWindowRules, WindowRef};
|
||||
use crate::layout::{
|
||||
LayoutElement, LayoutElementRenderElement, LayoutElementSnapshotRenderElements,
|
||||
};
|
||||
use crate::layout::{LayoutElement, LayoutElementRenderElement, LayoutElementRenderSnapshot};
|
||||
use crate::niri::WindowOffscreenId;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
use crate::render_helpers::surface::render_and_save_from_surface_tree;
|
||||
use crate::render_helpers::{RenderSnapshot, RenderTarget};
|
||||
use crate::render_helpers::surface::render_snapshot_from_surface_tree;
|
||||
use crate::render_helpers::{BakedBuffer, RenderSnapshot, RenderTarget};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mapped {
|
||||
@@ -43,7 +42,7 @@ pub struct Mapped {
|
||||
block_out_buffer: RefCell<SolidColorBuffer>,
|
||||
|
||||
/// Snapshot of the last render for use in the close animation.
|
||||
last_render: RefCell<RenderSnapshot<LayoutElementSnapshotRenderElements>>,
|
||||
last_render: RefCell<LayoutElementRenderSnapshot>,
|
||||
}
|
||||
|
||||
impl Mapped {
|
||||
@@ -95,6 +94,42 @@ impl Mapped {
|
||||
self.is_focused = is_focused;
|
||||
self.need_to_recompute_rules = true;
|
||||
}
|
||||
|
||||
pub fn render_and_store_snapshot(&self, renderer: &mut GlesRenderer) {
|
||||
let mut snapshot = self.last_render.borrow_mut();
|
||||
if !snapshot.contents.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
snapshot.contents.clear();
|
||||
snapshot.blocked_out_contents.clear();
|
||||
snapshot.block_out_from = self.rules.block_out_from;
|
||||
|
||||
let mut buffer = self.block_out_buffer.borrow_mut();
|
||||
buffer.resize(self.window.geometry().size);
|
||||
snapshot.blocked_out_contents = vec![BakedBuffer {
|
||||
buffer: buffer.clone(),
|
||||
location: Point::from((0, 0)),
|
||||
src: None,
|
||||
dst: None,
|
||||
}];
|
||||
|
||||
let buf_pos = self.window.geometry().loc.upscale(-1);
|
||||
|
||||
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;
|
||||
|
||||
render_snapshot_from_surface_tree(
|
||||
renderer,
|
||||
popup.wl_surface(),
|
||||
buf_pos + offset,
|
||||
&mut snapshot.contents,
|
||||
);
|
||||
}
|
||||
|
||||
render_snapshot_from_surface_tree(renderer, surface, buf_pos, &mut snapshot.contents);
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutElement for Mapped {
|
||||
@@ -131,10 +166,9 @@ impl LayoutElement for Mapped {
|
||||
Some(BlockOutFrom::ScreenCapture) => target != RenderTarget::Output,
|
||||
};
|
||||
|
||||
let mut buffer = self.block_out_buffer.borrow_mut();
|
||||
buffer.resize(self.window.geometry().size);
|
||||
|
||||
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),
|
||||
@@ -146,64 +180,11 @@ impl LayoutElement for Mapped {
|
||||
} else {
|
||||
let buf_pos = location - self.window.geometry().loc;
|
||||
let buf_pos = buf_pos.to_physical_precise_round(scale);
|
||||
|
||||
let mut elements = vec![];
|
||||
|
||||
// If we're rendering for output, save into last_render.
|
||||
let mut last_render = self.last_render.borrow_mut();
|
||||
// FIXME: when preview-render is active, last render contents will never update.
|
||||
let mut storage = if target == RenderTarget::Output {
|
||||
last_render.contents.clear();
|
||||
last_render.block_out_from = self.rules.block_out_from;
|
||||
last_render.blocked_out_contents = vec![SolidColorRenderElement::from_buffer(
|
||||
&buffer,
|
||||
(0, 0),
|
||||
scale,
|
||||
alpha,
|
||||
Kind::Unspecified,
|
||||
)
|
||||
.into()];
|
||||
|
||||
Some(&mut last_render.contents)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
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)
|
||||
.to_physical_precise_round(scale);
|
||||
|
||||
render_and_save_from_surface_tree(
|
||||
renderer,
|
||||
popup.wl_surface(),
|
||||
buf_pos,
|
||||
offset,
|
||||
scale,
|
||||
alpha,
|
||||
Kind::Unspecified,
|
||||
&mut elements,
|
||||
&mut storage,
|
||||
);
|
||||
}
|
||||
|
||||
render_and_save_from_surface_tree(
|
||||
renderer,
|
||||
surface,
|
||||
buf_pos,
|
||||
Point::from((0., 0.)),
|
||||
scale,
|
||||
alpha,
|
||||
Kind::Unspecified,
|
||||
&mut elements,
|
||||
&mut storage,
|
||||
);
|
||||
|
||||
elements
|
||||
self.window.render_elements(renderer, buf_pos, scale, alpha)
|
||||
}
|
||||
}
|
||||
|
||||
fn take_last_render(&self) -> RenderSnapshot<LayoutElementSnapshotRenderElements> {
|
||||
fn take_last_render(&self) -> LayoutElementRenderSnapshot {
|
||||
self.last_render.take()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user