mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
Cache texture in OpenAnimation
Don't recreate it unless the size changes. This lays the groundwork for also tracking damage in the future.
This commit is contained in:
@@ -2,27 +2,27 @@ use std::collections::HashMap;
|
||||
|
||||
use anyhow::Context as _;
|
||||
use glam::{Mat3, Vec2};
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::renderer::element::utils::{
|
||||
Relocate, RelocateRenderElement, RescaleRenderElement,
|
||||
};
|
||||
use smithay::backend::renderer::element::{Kind, RenderElement};
|
||||
use smithay::backend::renderer::gles::{GlesRenderer, Uniform};
|
||||
use smithay::backend::renderer::Texture;
|
||||
use smithay::utils::{Logical, Point, Rectangle, Scale, Size, Transform};
|
||||
use smithay::utils::{Logical, Point, Rectangle, Scale, Size};
|
||||
|
||||
use crate::animation::Animation;
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::offscreen::OffscreenBuffer;
|
||||
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||
use crate::render_helpers::render_to_encompassing_texture;
|
||||
use crate::render_helpers::shader_element::ShaderRenderElement;
|
||||
use crate::render_helpers::shaders::{mat3_uniform, ProgramType, Shaders};
|
||||
use crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};
|
||||
use crate::render_helpers::texture::TextureRenderElement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OpenAnimation {
|
||||
anim: Animation,
|
||||
random_seed: f32,
|
||||
buffer: OffscreenBuffer,
|
||||
}
|
||||
|
||||
niri_render_elements! {
|
||||
@@ -37,6 +37,7 @@ impl OpenAnimation {
|
||||
Self {
|
||||
anim,
|
||||
random_seed: fastrand::f32(),
|
||||
buffer: OffscreenBuffer::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,17 +60,15 @@ impl OpenAnimation {
|
||||
let progress = self.anim.value();
|
||||
let clamped_progress = self.anim.clamped_value().clamp(0., 1.);
|
||||
|
||||
let (texture, _sync_point, geo) = render_to_encompassing_texture(
|
||||
renderer,
|
||||
scale,
|
||||
Transform::Normal,
|
||||
Fourcc::Abgr8888,
|
||||
elements,
|
||||
)
|
||||
.context("error rendering to texture")?;
|
||||
let (buffer, _sync_point, offset) = self
|
||||
.buffer
|
||||
.render(renderer, scale, elements)
|
||||
.context("error rendering to offscreen buffer")?;
|
||||
|
||||
let offset = geo.loc.to_f64().to_logical(scale);
|
||||
let texture_size = geo.size.to_f64().to_logical(scale);
|
||||
// OffscreenBuffer renders with Transform::Normal and the scale that we passed, so we can
|
||||
// assume that below.
|
||||
let texture = buffer.texture();
|
||||
let texture_size = buffer.logical_size();
|
||||
|
||||
if Shaders::get(renderer).program(ProgramType::Open).is_some() {
|
||||
let mut area = Rectangle::new(location + offset, texture_size);
|
||||
@@ -120,8 +119,6 @@ impl OpenAnimation {
|
||||
.into());
|
||||
}
|
||||
|
||||
let buffer =
|
||||
TextureBuffer::from_texture(renderer, texture, scale, Transform::Normal, Vec::new());
|
||||
let elem = TextureRenderElement::from_texture_buffer(
|
||||
buffer,
|
||||
Point::from((0., 0.)),
|
||||
|
||||
+93
-228
@@ -1,251 +1,116 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use anyhow::Context as _;
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
|
||||
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
|
||||
use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer};
|
||||
use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};
|
||||
use smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
|
||||
use smithay::backend::renderer::sync::SyncPoint;
|
||||
use smithay::backend::renderer::{Bind as _, Offscreen as _, Texture as _};
|
||||
use smithay::utils::{Logical, Point, Scale, Transform};
|
||||
|
||||
use super::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||
use super::render_to_texture;
|
||||
use super::renderer::AsGlesFrame;
|
||||
use super::texture::{TextureBuffer, TextureRenderElement};
|
||||
use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
|
||||
use super::texture::TextureBuffer;
|
||||
use super::{encompassing_geo, render_elements};
|
||||
|
||||
/// Renders elements into an off-screen buffer.
|
||||
/// Buffer for offscreen rendering.
|
||||
#[derive(Debug)]
|
||||
pub struct OffscreenRenderElement {
|
||||
// The texture, if rendering succeeded.
|
||||
texture: Option<PrimaryGpuTextureRenderElement>,
|
||||
// The fallback buffer in case the rendering fails.
|
||||
fallback: SolidColorRenderElement,
|
||||
pub struct OffscreenBuffer {
|
||||
/// The cached texture buffer.
|
||||
///
|
||||
/// Lazily created when `render` is called. Recreated when necessary.
|
||||
buffer: RefCell<Option<TextureBuffer<GlesTexture>>>,
|
||||
}
|
||||
|
||||
impl OffscreenRenderElement {
|
||||
pub fn new(
|
||||
impl OffscreenBuffer {
|
||||
pub fn render(
|
||||
&self,
|
||||
renderer: &mut GlesRenderer,
|
||||
scale: i32,
|
||||
scale: Scale<f64>,
|
||||
elements: &[impl RenderElement<GlesRenderer>],
|
||||
result_alpha: f32,
|
||||
) -> Self {
|
||||
let _span = tracy_client::span!("OffscreenRenderElement::new");
|
||||
|
||||
let geo = elements
|
||||
.iter()
|
||||
.map(|ele| ele.geometry(Scale::from(f64::from(scale))))
|
||||
.reduce(|a, b| a.merge(b))
|
||||
.unwrap_or_default();
|
||||
let logical_size = geo.size.to_logical(scale);
|
||||
|
||||
let fallback_buffer = SolidColorBuffer::new(logical_size, [1., 0., 0., 1.]);
|
||||
let fallback = SolidColorRenderElement::from_buffer(
|
||||
&fallback_buffer,
|
||||
geo.loc,
|
||||
Scale::from(scale as f64),
|
||||
result_alpha,
|
||||
Kind::Unspecified,
|
||||
);
|
||||
) -> anyhow::Result<(TextureBuffer<GlesTexture>, SyncPoint, Point<f64, Logical>)> {
|
||||
let _span = tracy_client::span!("OffscreenBuffer::render");
|
||||
|
||||
let geo = encompassing_geo(scale, elements.iter());
|
||||
let elements = elements.iter().rev().map(|ele| {
|
||||
RelocateRenderElement::from_element(ele, (-geo.loc.x, -geo.loc.y), Relocate::Relative)
|
||||
RelocateRenderElement::from_element(ele, geo.loc.upscale(-1), Relocate::Relative)
|
||||
});
|
||||
|
||||
match render_to_texture(
|
||||
let buffer_size = geo.size.to_logical(1).to_buffer(1, Transform::Normal);
|
||||
let offset = geo.loc.to_f64().to_logical(scale);
|
||||
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
|
||||
// Check if we need to create or recreate the texture.
|
||||
let size_string;
|
||||
let mut reason = "";
|
||||
if let Some(buf) = buffer.as_mut() {
|
||||
let old_size = buf.texture().size();
|
||||
if old_size != buffer_size {
|
||||
size_string = format!(
|
||||
"size changed from {} × {} to {} × {}",
|
||||
old_size.w, old_size.h, buffer_size.w, buffer_size.h
|
||||
);
|
||||
reason = &size_string;
|
||||
|
||||
*buffer = None;
|
||||
} else if !buf.is_texture_reference_unique() {
|
||||
reason = "not unique";
|
||||
|
||||
*buffer = None;
|
||||
}
|
||||
} else {
|
||||
reason = "first render";
|
||||
}
|
||||
|
||||
let buffer = if let Some(buffer) = buffer.as_mut() {
|
||||
buffer
|
||||
} else {
|
||||
trace!("creating new texture: {reason}");
|
||||
let span = tracy_client::span!("creating offscreen buffer");
|
||||
span.emit_text(reason);
|
||||
|
||||
let texture: GlesTexture = renderer
|
||||
.create_buffer(Fourcc::Abgr8888, buffer_size)
|
||||
.context("error creating texture")?;
|
||||
|
||||
buffer.insert(TextureBuffer::from_texture(
|
||||
renderer,
|
||||
texture,
|
||||
scale,
|
||||
Transform::Normal,
|
||||
Vec::new(),
|
||||
))
|
||||
};
|
||||
|
||||
// Update the texture scale.
|
||||
buffer.set_texture_scale(scale);
|
||||
|
||||
// Increment the commit counter since we're rendering new contents to the buffer.
|
||||
buffer.increment_commit_counter();
|
||||
|
||||
// Render to the buffer.
|
||||
let mut texture = buffer.texture().clone();
|
||||
let mut target = renderer
|
||||
.bind(&mut texture)
|
||||
.context("error binding texture")?;
|
||||
|
||||
let sync_point = render_elements(
|
||||
renderer,
|
||||
&mut target,
|
||||
geo.size,
|
||||
Scale::from(scale as f64),
|
||||
scale,
|
||||
Transform::Normal,
|
||||
Fourcc::Abgr8888,
|
||||
elements,
|
||||
) {
|
||||
Ok((texture, _sync_point)) => {
|
||||
let buffer = TextureBuffer::from_texture(
|
||||
renderer,
|
||||
texture,
|
||||
scale as f64,
|
||||
Transform::Normal,
|
||||
Vec::new(),
|
||||
);
|
||||
let element = TextureRenderElement::from_texture_buffer(
|
||||
buffer,
|
||||
geo.loc.to_f64().to_logical(scale as f64),
|
||||
result_alpha,
|
||||
None,
|
||||
None,
|
||||
Kind::Unspecified,
|
||||
);
|
||||
Self {
|
||||
texture: Some(PrimaryGpuTextureRenderElement(element)),
|
||||
fallback,
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("error off-screening elements: {err:?}");
|
||||
Self {
|
||||
texture: None,
|
||||
fallback,
|
||||
}
|
||||
}
|
||||
}
|
||||
)?;
|
||||
|
||||
Ok((buffer.clone(), sync_point, offset))
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for OffscreenRenderElement {
|
||||
fn id(&self) -> &Id {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.id()
|
||||
} else {
|
||||
self.fallback.id()
|
||||
}
|
||||
}
|
||||
|
||||
fn current_commit(&self) -> CommitCounter {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.current_commit()
|
||||
} else {
|
||||
self.fallback.current_commit()
|
||||
}
|
||||
}
|
||||
|
||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.geometry(scale)
|
||||
} else {
|
||||
self.fallback.geometry(scale)
|
||||
}
|
||||
}
|
||||
|
||||
fn transform(&self) -> Transform {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.transform()
|
||||
} else {
|
||||
self.fallback.transform()
|
||||
}
|
||||
}
|
||||
|
||||
fn src(&self) -> Rectangle<f64, Buffer> {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.src()
|
||||
} else {
|
||||
self.fallback.src()
|
||||
}
|
||||
}
|
||||
|
||||
fn damage_since(
|
||||
&self,
|
||||
scale: Scale<f64>,
|
||||
commit: Option<CommitCounter>,
|
||||
) -> DamageSet<i32, Physical> {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.damage_since(scale, commit)
|
||||
} else {
|
||||
self.fallback.damage_since(scale, commit)
|
||||
}
|
||||
}
|
||||
|
||||
fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.opaque_regions(scale)
|
||||
} else {
|
||||
self.fallback.opaque_regions(scale)
|
||||
}
|
||||
}
|
||||
|
||||
fn alpha(&self) -> f32 {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.alpha()
|
||||
} else {
|
||||
self.fallback.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
fn kind(&self) -> Kind {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.kind()
|
||||
} else {
|
||||
self.fallback.kind()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderElement<GlesRenderer> for OffscreenRenderElement {
|
||||
fn draw(
|
||||
&self,
|
||||
frame: &mut GlesFrame<'_, '_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
damage: &[Rectangle<i32, Physical>],
|
||||
opaque_regions: &[Rectangle<i32, Physical>],
|
||||
) -> Result<(), GlesError> {
|
||||
let gles_frame = frame.as_gles_frame();
|
||||
if let Some(texture) = &self.texture {
|
||||
RenderElement::<GlesRenderer>::draw(
|
||||
texture,
|
||||
gles_frame,
|
||||
src,
|
||||
dst,
|
||||
damage,
|
||||
opaque_regions,
|
||||
)?;
|
||||
} else {
|
||||
RenderElement::<GlesRenderer>::draw(
|
||||
&self.fallback,
|
||||
gles_frame,
|
||||
src,
|
||||
dst,
|
||||
damage,
|
||||
opaque_regions,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.underlying_storage(renderer)
|
||||
} else {
|
||||
self.fallback.underlying_storage(renderer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'render> RenderElement<TtyRenderer<'render>> for OffscreenRenderElement {
|
||||
fn draw(
|
||||
&self,
|
||||
frame: &mut TtyFrame<'_, '_, '_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
damage: &[Rectangle<i32, Physical>],
|
||||
opaque_regions: &[Rectangle<i32, Physical>],
|
||||
) -> Result<(), TtyRendererError<'render>> {
|
||||
let gles_frame = frame.as_gles_frame();
|
||||
if let Some(texture) = &self.texture {
|
||||
RenderElement::<GlesRenderer>::draw(
|
||||
texture,
|
||||
gles_frame,
|
||||
src,
|
||||
dst,
|
||||
damage,
|
||||
opaque_regions,
|
||||
)?;
|
||||
} else {
|
||||
RenderElement::<GlesRenderer>::draw(
|
||||
&self.fallback,
|
||||
gles_frame,
|
||||
src,
|
||||
dst,
|
||||
damage,
|
||||
opaque_regions,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn underlying_storage(&self, renderer: &mut TtyRenderer<'render>) -> Option<UnderlyingStorage> {
|
||||
if let Some(texture) = &self.texture {
|
||||
texture.underlying_storage(renderer)
|
||||
} else {
|
||||
self.fallback.underlying_storage(renderer)
|
||||
impl Default for OffscreenBuffer {
|
||||
fn default() -> Self {
|
||||
OffscreenBuffer {
|
||||
buffer: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
|
||||
use smithay::backend::renderer::gles::GlesTexture;
|
||||
use smithay::backend::renderer::utils::{CommitCounter, OpaqueRegions};
|
||||
use smithay::backend::renderer::{Frame as _, ImportMem, Renderer, Texture};
|
||||
use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};
|
||||
@@ -104,6 +105,10 @@ impl<T> TextureBuffer<T> {
|
||||
pub fn set_texture_transform(&mut self, transform: Transform) {
|
||||
self.transform = transform;
|
||||
}
|
||||
|
||||
pub fn increment_commit_counter(&mut self) {
|
||||
self.commit_counter.increment();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Texture> TextureBuffer<T> {
|
||||
@@ -115,6 +120,12 @@ impl<T: Texture> TextureBuffer<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl TextureBuffer<GlesTexture> {
|
||||
pub fn is_texture_reference_unique(&mut self) -> bool {
|
||||
self.texture.is_unique_reference()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TextureRenderElement<T> {
|
||||
pub fn from_texture_buffer(
|
||||
buffer: TextureBuffer<T>,
|
||||
|
||||
Reference in New Issue
Block a user