forked from Mirror/niri
perf(blur): only re-render optimized blur when actually necessary
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use niri_config::utils::MergeWith as _;
|
||||
use niri_config::{Blur, Config, LayerRule};
|
||||
use niri_config::{Config, LayerRule};
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::renderer::element::surface::{
|
||||
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
|
||||
@@ -14,11 +14,10 @@ use super::ResolvedLayerRules;
|
||||
use crate::animation::Clock;
|
||||
use crate::layout::shadow::Shadow;
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::blur::element::BlurRenderElement;
|
||||
use crate::render_helpers::blur::element::{Blur, BlurRenderElement, CommitTracker};
|
||||
use crate::render_helpers::blur::EffectsFramebufffersUserData;
|
||||
use crate::render_helpers::clipped_surface::ClippedSurfaceRenderElement;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
use crate::render_helpers::shaders::Shaders;
|
||||
use crate::render_helpers::shadow::ShadowRenderElement;
|
||||
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use crate::render_helpers::{render_to_texture, RenderTarget, SplitElements};
|
||||
@@ -39,7 +38,7 @@ pub struct MappedLayer {
|
||||
shadow: Shadow,
|
||||
|
||||
/// Configuration for this layer's blur.
|
||||
blur_config: Blur,
|
||||
blur: Blur,
|
||||
|
||||
/// Size (used for blur).
|
||||
// TODO: move to standalone blur struct
|
||||
@@ -91,7 +90,7 @@ impl MappedLayer {
|
||||
scale,
|
||||
shadow: Shadow::new(shadow_config),
|
||||
clock,
|
||||
blur_config,
|
||||
blur: Blur::new(blur_config),
|
||||
size: Size::default(),
|
||||
}
|
||||
}
|
||||
@@ -106,7 +105,7 @@ impl MappedLayer {
|
||||
let mut blur_config = config.layout.blur;
|
||||
blur_config.on = false;
|
||||
blur_config.merge_with(&self.rules.blur);
|
||||
self.blur_config = blur_config;
|
||||
self.blur.update_config(blur_config);
|
||||
}
|
||||
|
||||
pub fn update_shaders(&mut self) {
|
||||
@@ -197,8 +196,11 @@ impl MappedLayer {
|
||||
let alpha = self.rules.opacity.unwrap_or(1.).clamp(0., 1.);
|
||||
let location = location + self.bob_offset();
|
||||
|
||||
// Normal surface elements used to render a texture for the ignore alpha pass inside the
|
||||
// blur shader.
|
||||
let mut gles_elems: Option<Vec<LayerSurfaceRenderElement<GlesRenderer>>> = None;
|
||||
let ignore_alpha = self.rules.blur.ignore_alpha.unwrap_or_default().0;
|
||||
let mut update_alpha_tex = ignore_alpha > 0.;
|
||||
|
||||
if target.should_block_out(self.rules.block_out_from) {
|
||||
// Round to physical pixels.
|
||||
@@ -240,98 +242,76 @@ impl MappedLayer {
|
||||
Kind::ScanoutCandidate,
|
||||
);
|
||||
|
||||
gles_elems = (ignore_alpha > 0.).then(|| {
|
||||
render_elements_from_surface_tree(
|
||||
// If there's been an update to our render elements, we need to render them again for
|
||||
// our blur ignore alpha pass.
|
||||
if ignore_alpha > 0.
|
||||
&& self
|
||||
.blur
|
||||
.maybe_update_commit_tracker(CommitTracker::from_elements(rv.normal.iter()))
|
||||
{
|
||||
gles_elems = Some(render_elements_from_surface_tree(
|
||||
renderer.as_gles_renderer(),
|
||||
surface,
|
||||
buf_pos.to_physical_precise_round(scale),
|
||||
scale,
|
||||
alpha,
|
||||
Kind::ScanoutCandidate,
|
||||
)
|
||||
});
|
||||
));
|
||||
} else {
|
||||
update_alpha_tex = false;
|
||||
}
|
||||
};
|
||||
|
||||
let blur_elem = (self.blur_config.on
|
||||
&& matches!(self.surface.layer(), Layer::Top | Layer::Overlay))
|
||||
let blur_elem = (matches!(self.surface.layer(), Layer::Top | Layer::Overlay)
|
||||
&& !target.should_block_out(self.rules.block_out_from))
|
||||
.then(|| {
|
||||
let fx_buffers = fx_buffers?;
|
||||
let fx_buffers = fx_buffers.borrow();
|
||||
|
||||
// TODO: respect sync point?
|
||||
let alpha_tex = (ignore_alpha > 0.)
|
||||
.then(|| {
|
||||
gles_elems.and_then(|gles_elems| {
|
||||
let transform = fx_buffers.transform();
|
||||
let alpha_tex = gles_elems
|
||||
.and_then(|gles_elems| {
|
||||
let transform = fx_buffers.transform();
|
||||
|
||||
render_to_texture(
|
||||
renderer.as_gles_renderer(),
|
||||
transform.transform_size(fx_buffers.output_size()),
|
||||
self.scale.into(),
|
||||
Transform::Normal,
|
||||
Fourcc::Abgr8888,
|
||||
gles_elems.into_iter(),
|
||||
)
|
||||
.inspect_err(|e| warn!("failed to render alpha tex: {e:?}"))
|
||||
.ok()
|
||||
})
|
||||
render_to_texture(
|
||||
renderer.as_gles_renderer(),
|
||||
transform.transform_size(fx_buffers.output_size()),
|
||||
self.scale.into(),
|
||||
Transform::Normal,
|
||||
Fourcc::Abgr8888,
|
||||
gles_elems.into_iter(),
|
||||
)
|
||||
.inspect_err(|e| warn!("failed to render alpha tex for layer surface: {e:?}"))
|
||||
.ok()
|
||||
})
|
||||
.flatten()
|
||||
.map(|r| r.0);
|
||||
|
||||
let radius = self.rules.geometry_corner_radius.unwrap_or_default();
|
||||
if update_alpha_tex {
|
||||
if let Some(alpha_tex) = alpha_tex {
|
||||
self.blur.set_alpha_tex(alpha_tex);
|
||||
} else {
|
||||
self.blur.clear_alpha_tex();
|
||||
}
|
||||
}
|
||||
|
||||
let blur_sample_area = Rectangle::new(location, self.size).to_i32_round();
|
||||
|
||||
let elem = BlurRenderElement::new_optimized(
|
||||
renderer,
|
||||
&fx_buffers,
|
||||
blur_sample_area,
|
||||
location.to_physical_precise_round(self.scale),
|
||||
self.rules
|
||||
.geometry_corner_radius
|
||||
.unwrap_or_default()
|
||||
.top_left,
|
||||
self.scale,
|
||||
self.blur_config,
|
||||
);
|
||||
|
||||
let geo = Rectangle::new(location, blur_sample_area.size.to_f64());
|
||||
|
||||
let clip_to_geometry =
|
||||
ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radius);
|
||||
|
||||
let clip_shader = (alpha_tex.is_some() || clip_to_geometry)
|
||||
.then(|| Shaders::get(renderer).clipped_surface.clone())
|
||||
.flatten();
|
||||
|
||||
let elem = if let Some(clip_shader) = clip_shader {
|
||||
let view_src = blur_sample_area.to_f64();
|
||||
let buf_size = fx_buffers
|
||||
.output_size()
|
||||
.to_f64()
|
||||
.to_logical(self.scale)
|
||||
.to_i32_round();
|
||||
|
||||
ClippedSurfaceRenderElement::new(
|
||||
elem,
|
||||
view_src,
|
||||
buf_size,
|
||||
self.scale.into(),
|
||||
geo,
|
||||
clip_shader,
|
||||
radius,
|
||||
alpha_tex,
|
||||
ignore_alpha,
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
elem.into()
|
||||
};
|
||||
|
||||
Some(elem)
|
||||
Some(
|
||||
self.blur
|
||||
.render(
|
||||
&fx_buffers,
|
||||
blur_sample_area,
|
||||
self.rules.geometry_corner_radius.unwrap_or_default(),
|
||||
self.scale,
|
||||
geo,
|
||||
)
|
||||
.map(Into::into),
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
.flatten()
|
||||
.into_iter();
|
||||
|
||||
let location = location.to_physical_precise_round(scale).to_logical(scale);
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use niri_config::utils::MergeWith as _;
|
||||
use niri_config::{Blur, Color, CornerRadius, GradientInterpolation};
|
||||
use niri_config::{Color, CornerRadius, GradientInterpolation};
|
||||
use niri_ipc::WindowLayout;
|
||||
use portable_atomic::AtomicU8;
|
||||
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
|
||||
@@ -22,7 +22,7 @@ use crate::animation::{Animation, Clock};
|
||||
use crate::layout::tab_indicator::{TabIndicator, TabIndicatorRenderElement, TabInfo};
|
||||
use crate::layout::SizingMode;
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::blur::element::BlurRenderElement;
|
||||
use crate::render_helpers::blur::element::{Blur, BlurRenderElement};
|
||||
use crate::render_helpers::blur::EffectsFramebufffersUserData;
|
||||
use crate::render_helpers::border::BorderRenderElement;
|
||||
use crate::render_helpers::clipped_surface::{ClippedSurfaceRenderElement, RoundedCornerDamage};
|
||||
@@ -408,8 +408,7 @@ pub struct Tile<W: LayoutElement> {
|
||||
window_size_override: WindowSizeOverride,
|
||||
|
||||
/// This tile's blur settings.
|
||||
// TODO: create a proper struct for this, like e.g. Shadow
|
||||
blur_config: Blur,
|
||||
blur: Blur,
|
||||
|
||||
/// Clock for driving animations.
|
||||
pub(super) clock: Clock,
|
||||
@@ -499,7 +498,7 @@ impl<W: LayoutElement> Tile<W> {
|
||||
border: FocusRing::new(border_config.into()),
|
||||
focus_ring: FocusRing::new(focus_ring_config),
|
||||
shadow: Shadow::new(shadow_config),
|
||||
blur_config,
|
||||
blur: Blur::new(blur_config),
|
||||
sizing_mode,
|
||||
fullscreen_backdrop: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]),
|
||||
restore_to_floating: false,
|
||||
@@ -568,7 +567,7 @@ impl<W: LayoutElement> Tile<W> {
|
||||
let mut blur_config = self.options.layout.blur;
|
||||
blur_config.on = false;
|
||||
blur_config.merge_with(&rules.blur);
|
||||
self.blur_config = blur_config;
|
||||
self.blur.update_config(blur_config);
|
||||
}
|
||||
|
||||
pub fn update_shaders(&mut self) {
|
||||
@@ -1768,6 +1767,7 @@ impl<W: LayoutElement> Tile<W> {
|
||||
let mut rounded_corner_damage = None;
|
||||
let has_border_shader = BorderRenderElement::has_shader(renderer);
|
||||
let geo = Rectangle::new(window_render_loc, window_size);
|
||||
let animated_geo = Rectangle::new(window_render_loc, animated_window_size);
|
||||
let clip_shader = clip_to_geometry
|
||||
.then(|| Shaders::get(renderer).clipped_surface.clone())
|
||||
.flatten();
|
||||
@@ -1927,49 +1927,18 @@ impl<W: LayoutElement> Tile<W> {
|
||||
.then(|| self.focus_ring.render(renderer, location).map(Into::into));
|
||||
let rv = rv.chain(elem.into_iter().flatten());
|
||||
|
||||
let blur_elem = self
|
||||
.blur_config
|
||||
.on
|
||||
.then(|| {
|
||||
let fx_buffers = fx_buffers?;
|
||||
let fx_buffers = fx_buffers.borrow();
|
||||
|
||||
let elem = BlurRenderElement::new_optimized(
|
||||
renderer,
|
||||
&fx_buffers,
|
||||
blur_sample_area.to_i32_round(),
|
||||
window_render_loc.to_physical(self.scale).to_i32_round(),
|
||||
radius.top_left,
|
||||
self.scale,
|
||||
self.blur_config,
|
||||
);
|
||||
|
||||
let elem = if clip_to_geometry {
|
||||
let view_src = blur_sample_area;
|
||||
let buf_size = fx_buffers
|
||||
.output_size()
|
||||
.to_f64()
|
||||
.to_logical(self.scale)
|
||||
.to_i32_round();
|
||||
ClippedSurfaceRenderElement::new(
|
||||
elem,
|
||||
view_src,
|
||||
buf_size,
|
||||
scale,
|
||||
geo,
|
||||
clip_shader?.clone(),
|
||||
let blur_elem = fx_buffers
|
||||
.and_then(|fx_buffers| {
|
||||
self.blur
|
||||
.render(
|
||||
&fx_buffers.borrow(),
|
||||
blur_sample_area.to_i32_round(),
|
||||
radius,
|
||||
None,
|
||||
0.,
|
||||
self.scale,
|
||||
animated_geo,
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
elem.into()
|
||||
};
|
||||
|
||||
Some(elem)
|
||||
.map(Into::into)
|
||||
})
|
||||
.flatten()
|
||||
.into_iter();
|
||||
|
||||
let elem = (expanded_progress < 1.)
|
||||
|
||||
@@ -1,58 +1,163 @@
|
||||
// Originally ported from https://github.com/nferhat/fht-compositor/blob/main/src/renderer/blur/element.rs
|
||||
|
||||
use niri_config::Blur;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use smithay::backend::renderer::element::texture::TextureRenderElement;
|
||||
use glam::{Mat3, Vec2};
|
||||
use niri_config::CornerRadius;
|
||||
|
||||
use pango::glib::property::PropertySet;
|
||||
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
|
||||
use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, GlesTexture, Uniform};
|
||||
use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};
|
||||
use smithay::backend::renderer::Renderer;
|
||||
use smithay::backend::renderer::gles::{
|
||||
ffi, GlesError, GlesFrame, GlesRenderer, GlesTexture, Uniform,
|
||||
};
|
||||
use smithay::backend::renderer::utils::{CommitCounter, OpaqueRegions};
|
||||
use smithay::backend::renderer::Texture;
|
||||
use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};
|
||||
|
||||
use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
|
||||
use crate::render_helpers::blur::EffectsFramebufffersUserData;
|
||||
use crate::render_helpers::render_data::RendererData;
|
||||
use crate::render_helpers::renderer::{AsGlesFrame, NiriRenderer};
|
||||
use crate::render_helpers::shaders::Shaders;
|
||||
use crate::render_helpers::renderer::AsGlesFrame;
|
||||
use crate::render_helpers::shaders::{mat3_uniform, Shaders};
|
||||
|
||||
use super::optimized_blur_texture_element::OptimizedBlurTextureElement;
|
||||
use super::{CurrentBuffer, EffectsFramebuffers};
|
||||
|
||||
/// Used for tracking commit counters of a collection of elements.
|
||||
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
||||
pub struct CommitTracker(HashMap<Id, CommitCounter>);
|
||||
|
||||
impl CommitTracker {
|
||||
pub fn new() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
|
||||
pub fn from_elements<'a, E: Element + 'a>(elems: impl Iterator<Item = &'a E>) -> Self {
|
||||
Self(
|
||||
elems
|
||||
.map(|e| (e.id().clone(), e.current_commit()))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn update<'a, E: Element + 'a>(&mut self, elems: impl Iterator<Item = &'a E>) {
|
||||
*self = Self::from_elements(elems);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BlurRenderElement {
|
||||
/// Use optimized blur, aka X-ray blur.
|
||||
///
|
||||
/// This technique relies on [`EffectsFramebuffers::optimized_blur`] to be populated. It will
|
||||
/// render this texture no matter what is below the blur render element.
|
||||
Optimized {
|
||||
tex: OptimizedBlurTextureElement,
|
||||
corner_radius: f32,
|
||||
noise: f32,
|
||||
pub struct Blur {
|
||||
config: niri_config::Blur,
|
||||
inner: RefCell<Option<BlurRenderElement>>,
|
||||
alpha_tex: RefCell<Option<GlesTexture>>,
|
||||
commit_tracker: RefCell<CommitTracker>,
|
||||
}
|
||||
|
||||
impl Blur {
|
||||
pub fn new(config: niri_config::Blur) -> Self {
|
||||
Self {
|
||||
config,
|
||||
inner: Default::default(),
|
||||
alpha_tex: Default::default(),
|
||||
commit_tracker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_update_commit_tracker(&self, other: CommitTracker) -> bool {
|
||||
if self.commit_tracker.borrow().eq(&other) {
|
||||
false
|
||||
} else {
|
||||
self.commit_tracker.set(other);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_config(&mut self, config: niri_config::Blur) {
|
||||
if self.config != config {
|
||||
self.inner.set(None);
|
||||
}
|
||||
|
||||
self.config = config;
|
||||
}
|
||||
|
||||
pub fn clear_alpha_tex(&self) {
|
||||
if let Some(inner) = self.inner.borrow_mut().as_mut() {
|
||||
if self.alpha_tex.borrow().is_some() {
|
||||
inner.damage_all();
|
||||
}
|
||||
}
|
||||
|
||||
self.alpha_tex.set(None);
|
||||
}
|
||||
|
||||
pub fn set_alpha_tex(&self, alpha_tex: GlesTexture) {
|
||||
self.alpha_tex.set(Some(alpha_tex));
|
||||
self.inner.set(None);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn render(
|
||||
&self,
|
||||
fx_buffers: &EffectsFramebuffers,
|
||||
sample_area: Rectangle<i32, Logical>,
|
||||
corner_radius: CornerRadius,
|
||||
scale: f64,
|
||||
output_size: Size<i32, Physical>,
|
||||
config: Blur,
|
||||
output_transform: Transform,
|
||||
},
|
||||
/// Use true blur.
|
||||
///
|
||||
/// When using this technique, the compositor will blur the current framebuffer contents that
|
||||
/// are below the [`BlurElement`] in order to display them. This adds an additional render step
|
||||
/// but provides true results with the blurred contents.
|
||||
TrueBlur {
|
||||
// we are just a funny texture element that generates the texture on RenderElement::draw
|
||||
id: Id,
|
||||
scale: f64,
|
||||
transform: Transform,
|
||||
src: Rectangle<f64, Logical>,
|
||||
size: Size<i32, Logical>,
|
||||
corner_radius: f32,
|
||||
loc: Point<i32, Physical>,
|
||||
config: Blur,
|
||||
// FIXME: Use DamageBag and expand it as needed?
|
||||
commit_counter: CommitCounter,
|
||||
fx_buffers: EffectsFramebufffersUserData,
|
||||
alpha_tex: Option<GlesTexture>,
|
||||
},
|
||||
geometry: Rectangle<f64, Logical>,
|
||||
) -> Option<BlurRenderElement> {
|
||||
if !self.config.on {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
||||
let Some(inner) = inner.as_mut() else {
|
||||
let elem = BlurRenderElement::new(
|
||||
fx_buffers,
|
||||
sample_area,
|
||||
corner_radius,
|
||||
scale,
|
||||
self.config,
|
||||
geometry,
|
||||
self.alpha_tex.borrow().clone(),
|
||||
);
|
||||
|
||||
*inner = Some(elem.clone());
|
||||
|
||||
return Some(elem);
|
||||
};
|
||||
|
||||
if inner.sample_area == sample_area
|
||||
&& inner.geometry == geometry
|
||||
&& inner.scale == scale
|
||||
&& inner.corner_radius == corner_radius
|
||||
&& fx_buffers.output_size().w == inner.texture.size().w
|
||||
&& fx_buffers.output_size().h == inner.texture.size().h
|
||||
{
|
||||
return Some(inner.clone());
|
||||
}
|
||||
|
||||
inner.texture = fx_buffers.optimized_blur.clone();
|
||||
inner.sample_area = sample_area;
|
||||
inner.alpha_tex = self.alpha_tex.borrow().clone();
|
||||
inner.scale = scale;
|
||||
inner.geometry = geometry;
|
||||
inner.damage_all();
|
||||
inner.update_uniforms(fx_buffers, &self.config);
|
||||
|
||||
Some(inner.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlurRenderElement {
|
||||
id: Id,
|
||||
texture: GlesTexture,
|
||||
uniforms: Vec<Uniform<'static>>,
|
||||
sample_area: Rectangle<i32, Logical>,
|
||||
alpha_tex: Option<GlesTexture>,
|
||||
scale: f64,
|
||||
commit: CommitCounter,
|
||||
corner_radius: CornerRadius,
|
||||
geometry: Rectangle<f64, Logical>,
|
||||
}
|
||||
|
||||
impl BlurRenderElement {
|
||||
@@ -65,165 +170,133 @@ impl BlurRenderElement {
|
||||
/// - Display outdated/wrong contents
|
||||
/// - Not display anything since the buffer will be empty.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new_optimized(
|
||||
renderer: &mut impl NiriRenderer,
|
||||
pub fn new(
|
||||
fx_buffers: &EffectsFramebuffers,
|
||||
sample_area: Rectangle<i32, Logical>,
|
||||
loc: Point<i32, Physical>,
|
||||
corner_radius: f32,
|
||||
corner_radius: CornerRadius,
|
||||
scale: f64,
|
||||
config: Blur,
|
||||
) -> Self {
|
||||
let texture = fx_buffers.optimized_blur.clone();
|
||||
|
||||
let scaled = sample_area.to_f64().upscale(scale);
|
||||
|
||||
let texture = TextureRenderElement::from_static_texture(
|
||||
Id::new(),
|
||||
renderer.as_gles_renderer().context_id(),
|
||||
loc.to_f64(),
|
||||
texture,
|
||||
1,
|
||||
Transform::Normal,
|
||||
Some(1.0),
|
||||
Some(scaled),
|
||||
Some(scaled.size.to_i32_ceil()),
|
||||
// NOTE: Since this is "optimized" blur, anything below the window will not be
|
||||
// rendered
|
||||
Some(vec![Rectangle::new(
|
||||
scaled.loc.to_i32_ceil(),
|
||||
scaled.size.to_i32_ceil(),
|
||||
)
|
||||
.to_buffer(1, Transform::Normal, &sample_area.size)]),
|
||||
Kind::Unspecified,
|
||||
);
|
||||
|
||||
Self::Optimized {
|
||||
tex: texture.into(),
|
||||
corner_radius,
|
||||
noise: config.noise.0 as f32,
|
||||
scale,
|
||||
output_size: fx_buffers.output_size,
|
||||
output_transform: fx_buffers.transform,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new_true(
|
||||
fx_buffers: EffectsFramebufffersUserData,
|
||||
sample_area: Rectangle<i32, Logical>,
|
||||
loc: Point<i32, Physical>,
|
||||
corner_radius: f32,
|
||||
scale: f64,
|
||||
config: Blur,
|
||||
zoom: f64,
|
||||
config: niri_config::Blur,
|
||||
geometry: Rectangle<f64, Logical>,
|
||||
alpha_tex: Option<GlesTexture>,
|
||||
) -> Self {
|
||||
let mut final_sample_area = sample_area.to_f64().upscale(zoom);
|
||||
let center = (fx_buffers.borrow().output_size.to_f64().to_logical(scale) / 2.).to_point();
|
||||
final_sample_area.loc.x = center.x - (center.x - sample_area.loc.x as f64) * zoom;
|
||||
final_sample_area.loc.y = center.y - (center.y - sample_area.loc.y as f64) * zoom;
|
||||
|
||||
Self::TrueBlur {
|
||||
let mut this = Self {
|
||||
id: Id::new(),
|
||||
scale,
|
||||
src: final_sample_area,
|
||||
transform: Transform::Normal,
|
||||
size: sample_area.size,
|
||||
corner_radius,
|
||||
loc,
|
||||
config,
|
||||
fx_buffers,
|
||||
commit_counter: CommitCounter::default(),
|
||||
texture: fx_buffers.optimized_blur.clone(),
|
||||
uniforms: Vec::with_capacity(7),
|
||||
alpha_tex,
|
||||
}
|
||||
sample_area,
|
||||
scale,
|
||||
corner_radius,
|
||||
geometry,
|
||||
commit: CommitCounter::default(),
|
||||
};
|
||||
|
||||
this.update_uniforms(fx_buffers, &config);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
fn update_uniforms(&mut self, fx_buffers: &EffectsFramebuffers, config: &niri_config::Blur) {
|
||||
let transform = Transform::Normal;
|
||||
|
||||
let elem_geo: Rectangle<i32, _> = self.sample_area.to_physical_precise_round(self.scale);
|
||||
let elem_geo_loc = Vec2::new(elem_geo.loc.x as f32, elem_geo.loc.y as f32);
|
||||
let elem_geo_size = Vec2::new(elem_geo.size.w as f32, elem_geo.size.h as f32);
|
||||
|
||||
let view_src = self.sample_area; // CORRECT
|
||||
let buf_size = fx_buffers.output_size().to_f64().to_logical(self.scale); // CORRECT
|
||||
let buf_size = Vec2::new(buf_size.w as f32, buf_size.h as f32);
|
||||
|
||||
let geo = self.geometry.to_physical_precise_round(self.scale);
|
||||
let geo_loc = Vec2::new(geo.loc.x, geo.loc.y);
|
||||
let geo_size = Vec2::new(geo.size.w, geo.size.h);
|
||||
|
||||
let src_loc = Vec2::new(view_src.loc.x as f32, view_src.loc.y as f32);
|
||||
let src_size = Vec2::new(view_src.size.w as f32, view_src.size.h as f32);
|
||||
|
||||
let transform_matrix = Mat3::from_translation(Vec2::new(0.5, 0.5))
|
||||
* Mat3::from_cols_array(transform.matrix().as_ref())
|
||||
* Mat3::from_translation(-Vec2::new(0.5, 0.5));
|
||||
|
||||
// FIXME: y_inverted
|
||||
let input_to_geo = transform_matrix * Mat3::from_scale(elem_geo_size / geo_size)
|
||||
* Mat3::from_translation((elem_geo_loc - geo_loc) / elem_geo_size)
|
||||
// Apply viewporter src.
|
||||
* Mat3::from_scale(buf_size / src_size)
|
||||
* Mat3::from_translation(-src_loc / buf_size);
|
||||
|
||||
self.uniforms = vec![
|
||||
Uniform::new("corner_radius", <[f32; 4]>::from(self.corner_radius)),
|
||||
Uniform::new("geo_size", geo_size.to_array()),
|
||||
Uniform::new("niri_scale", self.scale as f32),
|
||||
Uniform::new("noise", config.noise.0 as f32),
|
||||
mat3_uniform("input_to_geo", input_to_geo),
|
||||
Uniform::new(
|
||||
"ignore_alpha",
|
||||
if self.alpha_tex.is_some() {
|
||||
config.ignore_alpha.0 as f32
|
||||
} else {
|
||||
0.
|
||||
},
|
||||
),
|
||||
Uniform::new("alpha_tex", if self.alpha_tex.is_some() { 1 } else { 0 }),
|
||||
];
|
||||
}
|
||||
|
||||
fn damage_all(&mut self) {
|
||||
self.commit.increment()
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for BlurRenderElement {
|
||||
fn id(&self) -> &Id {
|
||||
match self {
|
||||
BlurRenderElement::Optimized { tex, .. } => tex.id(),
|
||||
BlurRenderElement::TrueBlur { id, .. } => id,
|
||||
}
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn current_commit(&self) -> CommitCounter {
|
||||
match self {
|
||||
BlurRenderElement::Optimized { tex, .. } => tex.current_commit(),
|
||||
BlurRenderElement::TrueBlur { commit_counter, .. } => *commit_counter,
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self, scale: Scale<f64>) -> Point<i32, Physical> {
|
||||
match self {
|
||||
BlurRenderElement::Optimized { tex, .. } => tex.location(scale),
|
||||
BlurRenderElement::TrueBlur { loc, .. } => *loc,
|
||||
}
|
||||
self.commit
|
||||
}
|
||||
|
||||
fn src(&self) -> Rectangle<f64, Buffer> {
|
||||
match self {
|
||||
BlurRenderElement::Optimized { tex, .. } => tex.src(),
|
||||
BlurRenderElement::TrueBlur {
|
||||
src,
|
||||
transform,
|
||||
size,
|
||||
scale,
|
||||
..
|
||||
} => src.to_buffer(*scale, *transform, &size.to_f64()),
|
||||
}
|
||||
self.sample_area.to_f64().to_buffer(
|
||||
self.scale,
|
||||
Transform::Normal,
|
||||
&self.sample_area.size.to_f64(),
|
||||
)
|
||||
}
|
||||
|
||||
fn transform(&self) -> Transform {
|
||||
match self {
|
||||
BlurRenderElement::Optimized { tex, .. } => tex.transform(),
|
||||
BlurRenderElement::TrueBlur { transform, .. } => *transform,
|
||||
}
|
||||
}
|
||||
|
||||
fn damage_since(
|
||||
&self,
|
||||
scale: Scale<f64>,
|
||||
commit: Option<CommitCounter>,
|
||||
) -> DamageSet<i32, Physical> {
|
||||
match self {
|
||||
BlurRenderElement::Optimized { tex, .. } => tex.damage_since(scale, commit),
|
||||
BlurRenderElement::TrueBlur { config, .. } => {
|
||||
let passes = config.passes;
|
||||
let radius = config.radius.0 as f32;
|
||||
|
||||
// Since the blur element samples from around itself, we must expand the damage it
|
||||
// induces to include any potential changes.
|
||||
let mut geometry = Rectangle::from_size(self.geometry(scale).size);
|
||||
let size = (2f32.powi(passes as i32 + 1) * radius).ceil() as i32;
|
||||
geometry.loc -= Point::from((size, size));
|
||||
geometry.size += Size::from((size, size)).upscale(2);
|
||||
|
||||
// FIXME: Damage tracking?
|
||||
DamageSet::from_slice(&[geometry])
|
||||
}
|
||||
}
|
||||
Transform::Normal
|
||||
}
|
||||
|
||||
fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {
|
||||
match self {
|
||||
BlurRenderElement::Optimized { tex, .. } => tex.opaque_regions(scale),
|
||||
BlurRenderElement::TrueBlur { .. } => {
|
||||
// Since we are rendering as true blur, we will draw whatever is behind the window
|
||||
OpaqueRegions::default()
|
||||
}
|
||||
if self.alpha_tex.is_some() {
|
||||
return OpaqueRegions::default();
|
||||
}
|
||||
|
||||
let geometry = self.geometry(scale);
|
||||
|
||||
let CornerRadius {
|
||||
top_left,
|
||||
top_right,
|
||||
bottom_right,
|
||||
bottom_left,
|
||||
} = self.corner_radius.scaled_by(scale.x as f32);
|
||||
|
||||
let largest_radius = top_left.max(top_right).max(bottom_right).max(bottom_left);
|
||||
|
||||
let rect = Rectangle::new(
|
||||
Point::new(top_left.ceil() as i32, top_left.ceil() as i32),
|
||||
(geometry.size.to_f64()
|
||||
- Size::new(largest_radius.ceil() as f64, largest_radius.ceil() as f64) * 2.)
|
||||
.to_i32_ceil(),
|
||||
);
|
||||
|
||||
OpaqueRegions::from_slice(&[rect])
|
||||
}
|
||||
|
||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||
match self {
|
||||
BlurRenderElement::Optimized { tex, .. } => tex.geometry(scale),
|
||||
BlurRenderElement::TrueBlur { loc, size, .. } => {
|
||||
Rectangle::new(*loc, size.to_physical_precise_round(scale))
|
||||
}
|
||||
}
|
||||
self.sample_area.to_f64().to_physical_precise_round(scale)
|
||||
}
|
||||
|
||||
fn alpha(&self) -> f32 {
|
||||
@@ -235,11 +308,12 @@ impl Element for BlurRenderElement {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn draw_true_blur(
|
||||
fx_buffers: &mut EffectsFramebuffers,
|
||||
gles_frame: &mut GlesFrame,
|
||||
config: &Blur,
|
||||
config: &niri_config::Blur,
|
||||
scale: f64,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
corner_radius: f32,
|
||||
@@ -336,47 +410,39 @@ impl RenderElement<GlesRenderer> for BlurRenderElement {
|
||||
) -> Result<(), GlesError> {
|
||||
let _span = trace_span!("blur_draw_gles").entered();
|
||||
|
||||
match self {
|
||||
Self::Optimized { tex, scale, .. } => {
|
||||
let downscaled_dst = Rectangle::new(
|
||||
dst.loc,
|
||||
Size::from((
|
||||
(dst.size.w as f64 / *scale) as i32,
|
||||
(dst.size.h as f64 / *scale) as i32,
|
||||
)),
|
||||
);
|
||||
let downscaled_dst = Rectangle::new(
|
||||
dst.loc,
|
||||
Size::from((
|
||||
(dst.size.w as f64 / self.scale) as i32,
|
||||
(dst.size.h as f64 / self.scale) as i32,
|
||||
)),
|
||||
);
|
||||
|
||||
<TextureRenderElement<GlesTexture> as RenderElement<GlesRenderer>>::draw(
|
||||
&tex.0,
|
||||
gles_frame,
|
||||
src,
|
||||
downscaled_dst,
|
||||
damage,
|
||||
opaque_regions,
|
||||
)
|
||||
}
|
||||
Self::TrueBlur {
|
||||
fx_buffers,
|
||||
scale,
|
||||
corner_radius,
|
||||
config,
|
||||
alpha_tex,
|
||||
..
|
||||
} => draw_true_blur(
|
||||
&mut fx_buffers.borrow_mut(),
|
||||
gles_frame,
|
||||
config,
|
||||
*scale,
|
||||
dst,
|
||||
*corner_radius,
|
||||
src,
|
||||
damage,
|
||||
opaque_regions,
|
||||
self.alpha(),
|
||||
false,
|
||||
alpha_tex.as_ref(),
|
||||
),
|
||||
let program = Shaders::get_from_frame(gles_frame)
|
||||
.blur_finish
|
||||
.clone()
|
||||
.expect("should be compiled");
|
||||
|
||||
if let Some(alpha_tex) = &self.alpha_tex {
|
||||
gles_frame.with_context(|gl| unsafe {
|
||||
gl.ActiveTexture(ffi::TEXTURE1);
|
||||
gl.BindTexture(ffi::TEXTURE_2D, alpha_tex.tex_id());
|
||||
gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32);
|
||||
gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_MAG_FILTER, ffi::LINEAR as i32);
|
||||
})?;
|
||||
}
|
||||
|
||||
gles_frame.render_texture_from_to(
|
||||
&self.texture,
|
||||
src,
|
||||
downscaled_dst,
|
||||
damage,
|
||||
opaque_regions,
|
||||
Transform::Normal,
|
||||
1.0,
|
||||
Some(&program),
|
||||
&self.uniforms,
|
||||
)
|
||||
}
|
||||
|
||||
fn underlying_storage(&self, _: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> {
|
||||
@@ -395,42 +461,14 @@ impl<'render> RenderElement<TtyRenderer<'render>> for BlurRenderElement {
|
||||
) -> Result<(), TtyRendererError<'render>> {
|
||||
let _span = trace_span!("blur_draw_tty").entered();
|
||||
|
||||
match self {
|
||||
Self::Optimized { .. } => {
|
||||
<BlurRenderElement as RenderElement<GlesRenderer>>::draw(
|
||||
self,
|
||||
frame.as_gles_frame(),
|
||||
src,
|
||||
dst,
|
||||
damage,
|
||||
opaque_regions,
|
||||
)?;
|
||||
}
|
||||
|
||||
Self::TrueBlur {
|
||||
fx_buffers,
|
||||
scale,
|
||||
corner_radius,
|
||||
config,
|
||||
alpha_tex,
|
||||
..
|
||||
} => {
|
||||
draw_true_blur(
|
||||
&mut fx_buffers.borrow_mut(),
|
||||
frame.as_gles_frame(),
|
||||
config,
|
||||
*scale,
|
||||
dst,
|
||||
*corner_radius,
|
||||
src,
|
||||
damage,
|
||||
opaque_regions,
|
||||
self.alpha(),
|
||||
true,
|
||||
alpha_tex.as_ref(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
<BlurRenderElement as RenderElement<GlesRenderer>>::draw(
|
||||
self,
|
||||
frame.as_gles_frame(),
|
||||
src,
|
||||
dst,
|
||||
damage,
|
||||
opaque_regions,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -17,10 +17,46 @@ uniform samplerExternalOES tex;
|
||||
uniform sampler2D tex;
|
||||
#endif
|
||||
|
||||
#if defined(EXTERNAL)
|
||||
uniform samplerExternalOES alpha_tex;
|
||||
#else
|
||||
uniform sampler2D alpha_tex;
|
||||
#endif
|
||||
|
||||
uniform float alpha;
|
||||
varying vec2 v_coords;
|
||||
|
||||
uniform vec4 corner_radius;
|
||||
uniform mat3 input_to_geo;
|
||||
uniform vec2 geo_size;
|
||||
uniform float niri_scale;
|
||||
uniform float noise;
|
||||
uniform float ignore_alpha;
|
||||
|
||||
float rounding_alpha(vec2 coords, vec2 size) {
|
||||
vec2 center;
|
||||
float radius;
|
||||
|
||||
if (coords.x < corner_radius.x && coords.y < corner_radius.x) {
|
||||
radius = corner_radius.x;
|
||||
center = vec2(radius, radius);
|
||||
} else if (size.x - corner_radius.y < coords.x && coords.y < corner_radius.y) {
|
||||
radius = corner_radius.y;
|
||||
center = vec2(size.x - radius, radius);
|
||||
} else if (size.x - corner_radius.z < coords.x && size.y - corner_radius.z < coords.y) {
|
||||
radius = corner_radius.z;
|
||||
center = vec2(size.x - radius, size.y - radius);
|
||||
} else if (coords.x < corner_radius.w && size.y - corner_radius.w < coords.y) {
|
||||
radius = corner_radius.w;
|
||||
center = vec2(radius, size.y - radius);
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
float dist = distance(coords, center);
|
||||
float half_px = 0.5 / niri_scale;
|
||||
return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);
|
||||
}
|
||||
|
||||
// Noise function copied from hyprland.
|
||||
// I like the effect it gave, can be tweaked further
|
||||
@@ -42,6 +78,8 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0);
|
||||
|
||||
// Sample the texture.
|
||||
vec4 color = texture2D(tex, v_coords);
|
||||
|
||||
@@ -49,6 +87,18 @@ void main() {
|
||||
color = vec4(color.rgb, 1.0);
|
||||
#endif
|
||||
|
||||
if (coords_geo.x < 0.0 || 1.0 < coords_geo.x || coords_geo.y < 0.0 || 1.0 < coords_geo.y) {
|
||||
// Clip outside geometry.
|
||||
color = vec4(0.0);
|
||||
} else {
|
||||
// Apply corner rounding inside geometry.
|
||||
color = color * rounding_alpha(coords_geo.xy * geo_size, geo_size);
|
||||
}
|
||||
|
||||
if (color.a <= 0.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
if (noise > 0.0) {
|
||||
// Add noise fx
|
||||
// This can be used to achieve a glass look
|
||||
@@ -57,11 +107,8 @@ void main() {
|
||||
color.rgb += noiseAmount * noise;
|
||||
}
|
||||
|
||||
color *= alpha;
|
||||
|
||||
if (color.a <= 0.0) {
|
||||
discard;
|
||||
}
|
||||
color *= alpha;
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
|
||||
@@ -13,12 +13,6 @@ uniform samplerExternalOES tex;
|
||||
uniform sampler2D tex;
|
||||
#endif
|
||||
|
||||
#if defined(EXTERNAL)
|
||||
uniform samplerExternalOES alpha_tex;
|
||||
#else
|
||||
uniform sampler2D alpha_tex;
|
||||
#endif
|
||||
|
||||
uniform float alpha;
|
||||
varying vec2 v_coords;
|
||||
|
||||
@@ -31,7 +25,6 @@ uniform float niri_scale;
|
||||
uniform vec2 geo_size;
|
||||
uniform vec4 corner_radius;
|
||||
uniform mat3 input_to_geo;
|
||||
uniform float ignore_alpha;
|
||||
|
||||
float rounding_alpha(vec2 coords, vec2 size) {
|
||||
vec2 center;
|
||||
@@ -59,17 +52,6 @@ float rounding_alpha(vec2 coords, vec2 size) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
if (alpha <= 0.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
if (ignore_alpha > 0.0) {
|
||||
vec4 alpha_color = texture2D(alpha_tex, v_coords);
|
||||
if (alpha_color.a < ignore_alpha) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0);
|
||||
|
||||
// Sample the texture.
|
||||
|
||||
@@ -88,8 +88,6 @@ impl Shaders {
|
||||
UniformName::new("geo_size", UniformType::_2f),
|
||||
UniformName::new("corner_radius", UniformType::_4f),
|
||||
UniformName::new("input_to_geo", UniformType::Matrix3x3),
|
||||
UniformName::new("alpha_tex", UniformType::_1i),
|
||||
UniformName::new("ignore_alpha", UniformType::_1f),
|
||||
],
|
||||
)
|
||||
.map_err(|err| {
|
||||
@@ -109,6 +107,12 @@ impl Shaders {
|
||||
&[
|
||||
UniformName::new("alpha", UniformType::_1f),
|
||||
UniformName::new("noise", UniformType::_1f),
|
||||
UniformName::new("niri_scale", UniformType::_1f),
|
||||
UniformName::new("geo_size", UniformType::_2f),
|
||||
UniformName::new("corner_radius", UniformType::_4f),
|
||||
UniformName::new("input_to_geo", UniformType::Matrix3x3),
|
||||
UniformName::new("alpha_tex", UniformType::_1i),
|
||||
UniformName::new("ignore_alpha", UniformType::_1f),
|
||||
],
|
||||
)
|
||||
.map_err(|e| warn!("error compiling clipped surface shader: {e:?}"))
|
||||
|
||||
Reference in New Issue
Block a user