element/wayland: Add blur background-effect
This commit is contained in:
parent
e4c0716951
commit
a9bf89d8b2
10 changed files with 747 additions and 5 deletions
|
|
@ -19,6 +19,7 @@ use crate::{
|
|||
shadow::{SHADOW_SHADER, ShadowShader},
|
||||
wayland::{
|
||||
SurfaceRenderElement,
|
||||
blur_effect::BlurShaders,
|
||||
clipped_surface::{CLIPPING_SHADER, ClippingShader},
|
||||
push_render_elements_from_surface_tree,
|
||||
},
|
||||
|
|
@ -425,6 +426,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> {
|
|||
UniformName::new("geo_size", UniformType::_2f),
|
||||
UniformName::new("corner_radius", UniformType::_4f),
|
||||
UniformName::new("input_to_geo", UniformType::Matrix3x3),
|
||||
UniformName::new("noise", UniformType::_1f),
|
||||
],
|
||||
)?;
|
||||
let shadow_shader = renderer.compile_custom_pixel_shader(
|
||||
|
|
@ -440,6 +442,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> {
|
|||
UniformName::new("window_corner_radius", UniformType::_4f),
|
||||
],
|
||||
)?;
|
||||
let blur_shaders = BlurShaders::compile(renderer)?;
|
||||
|
||||
let egl_context = renderer.egl_context();
|
||||
egl_context
|
||||
|
|
@ -457,6 +460,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> {
|
|||
egl_context
|
||||
.user_data()
|
||||
.insert_if_missing(|| ShadowShader(shadow_shader));
|
||||
egl_context.user_data().insert_if_missing(|| blur_shaders);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
26
src/backend/render/shaders/blur_downsample.frag
Normal file
26
src/backend/render/shaders/blur_downsample.frag
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#version 100
|
||||
|
||||
//_DEFINES_
|
||||
|
||||
precision highp float;
|
||||
|
||||
varying vec2 v_coords;
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec2 half_pixel;
|
||||
uniform float offset;
|
||||
|
||||
uniform float alpha;
|
||||
#if defined(DEBUG_FLAGS)
|
||||
uniform float tint;
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
vec4 sum = texture2D(tex, v_coords) * 4.0;
|
||||
sum += texture2D(tex, v_coords - half_pixel * offset);
|
||||
sum += texture2D(tex, v_coords + half_pixel * offset);
|
||||
sum += texture2D(tex, v_coords + vec2(half_pixel.x, -half_pixel.y) * offset);
|
||||
sum += texture2D(tex, v_coords - vec2(half_pixel.x, -half_pixel.y) * offset);
|
||||
|
||||
gl_FragColor = sum / sum.a;
|
||||
}
|
||||
27
src/backend/render/shaders/blur_upsample.frag
Normal file
27
src/backend/render/shaders/blur_upsample.frag
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#version 100
|
||||
|
||||
precision highp float;
|
||||
|
||||
varying vec2 v_coords;
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec2 half_pixel;
|
||||
uniform float offset;
|
||||
|
||||
uniform float alpha;
|
||||
#if defined(DEBUG_FLAGS)
|
||||
uniform float tint;
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
vec4 sum = texture2D(tex, v_coords + vec2(-half_pixel.x * 2.0, 0.0) * offset);
|
||||
sum += texture2D(tex, v_coords + vec2(-half_pixel.x, half_pixel.y) * offset) * 2.0;
|
||||
sum += texture2D(tex, v_coords + vec2(0.0, half_pixel.y * 2.0) * offset);
|
||||
sum += texture2D(tex, v_coords + vec2(half_pixel.x, half_pixel.y) * offset) * 2.0;
|
||||
sum += texture2D(tex, v_coords + vec2(half_pixel.x * 2.0, 0.0) * offset);
|
||||
sum += texture2D(tex, v_coords + vec2(half_pixel.x, -half_pixel.y) * offset) * 2.0;
|
||||
sum += texture2D(tex, v_coords + vec2(0.0, -half_pixel.y * 2.0) * offset);
|
||||
sum += texture2D(tex, v_coords + vec2(-half_pixel.x, -half_pixel.y) * offset) * 2.0;
|
||||
|
||||
gl_FragColor = sum / sum.a;
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ uniform float tint;
|
|||
uniform vec2 geo_size;
|
||||
uniform vec4 corner_radius;
|
||||
uniform mat3 input_to_geo;
|
||||
uniform float noise;
|
||||
|
||||
float rounding_alpha(vec2 coords, vec2 size) {
|
||||
vec2 center;
|
||||
|
|
@ -51,15 +52,26 @@ float rounding_alpha(vec2 coords, vec2 size) {
|
|||
return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0);
|
||||
float hash(vec2 p) {
|
||||
vec3 p3 = fract(vec3(p.xyx) * 727.727);
|
||||
p3 += dot(p3, p3.xyz + 33.33);
|
||||
return fract((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
void main() {
|
||||
if (alpha <= 0.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// Sample the texture.
|
||||
vec4 color = texture2D(tex, v_coords);
|
||||
#if defined(NO_ALPHA)
|
||||
color = vec4(color.rgb, 1.0);
|
||||
#endif
|
||||
if (color.a <= 0.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0);
|
||||
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);
|
||||
|
|
@ -68,8 +80,20 @@ void main() {
|
|||
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
|
||||
float noiseHash = hash(v_coords);
|
||||
float noiseAmount = (mod(noiseHash, 1.0) - 0.5);
|
||||
color.rgb += noiseAmount * noise;
|
||||
}
|
||||
|
||||
// Apply final alpha and tint.
|
||||
color = color * alpha;
|
||||
color *= alpha;
|
||||
|
||||
#if defined(DEBUG_FLAGS)
|
||||
if (tint == 1.0)
|
||||
|
|
|
|||
578
src/backend/render/wayland/blur_effect.rs
Normal file
578
src/backend/render/wayland/blur_effect.rs
Normal file
|
|
@ -0,0 +1,578 @@
|
|||
use std::{
|
||||
borrow::{Borrow, BorrowMut},
|
||||
sync::{
|
||||
Arc, Mutex,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
};
|
||||
|
||||
use cgmath::{Matrix3, SquareMatrix, Vector2};
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::Fourcc,
|
||||
renderer::{
|
||||
Bind, BlitFrame, Color32F, ContextId, Frame, FrameContext, ImportAll, Offscreen,
|
||||
Renderer, Texture, TextureFilter,
|
||||
element::{Element, Id, Kind, RenderElement},
|
||||
gles::{
|
||||
GlesError, GlesFrame, GlesRenderer, GlesTexProgram, GlesTexture, Uniform,
|
||||
UniformName, UniformType, UniformValue, ffi,
|
||||
},
|
||||
sync::SyncPoint,
|
||||
utils::{CommitCounter, DamageSet},
|
||||
},
|
||||
},
|
||||
utils::{
|
||||
Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform, user_data::UserDataMap,
|
||||
},
|
||||
wayland::compositor::SurfaceData,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::render::{element::AsGlowRenderer, wayland::clipped_surface::ClippingShader},
|
||||
wayland::handlers::background_effect::ComputedBlurRegionCachedState,
|
||||
};
|
||||
|
||||
pub static BLUR_DOWNSAMPLE_SHADER: &str = include_str!("../shaders/blur_downsample.frag");
|
||||
pub static BLUR_UPSAMPLE_SHADER: &str = include_str!("../shaders/blur_upsample.frag");
|
||||
|
||||
const PASSES: usize = 3;
|
||||
const OFFSET: f64 = 1.1;
|
||||
const NOISE: f32 = 0.03;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlurShaders {
|
||||
down: GlesTexProgram,
|
||||
up: GlesTexProgram,
|
||||
}
|
||||
|
||||
impl BlurShaders {
|
||||
pub fn compile(renderer: &mut GlesRenderer) -> Result<BlurShaders, GlesError> {
|
||||
let up = renderer.compile_custom_texture_shader(
|
||||
BLUR_UPSAMPLE_SHADER,
|
||||
&[
|
||||
UniformName::new("half_pixel", UniformType::_2f),
|
||||
UniformName::new("offset", UniformType::_1f),
|
||||
],
|
||||
)?;
|
||||
let down = renderer.compile_custom_texture_shader(
|
||||
BLUR_DOWNSAMPLE_SHADER,
|
||||
&[
|
||||
UniformName::new("half_pixel", UniformType::_2f),
|
||||
UniformName::new("offset", UniformType::_1f),
|
||||
],
|
||||
)?;
|
||||
|
||||
Ok(BlurShaders { up, down })
|
||||
}
|
||||
|
||||
pub fn get<R: AsGlowRenderer>(renderer: &R) -> Self {
|
||||
Borrow::<GlesRenderer>::borrow(renderer.glow_renderer())
|
||||
.egl_context()
|
||||
.user_data()
|
||||
.get::<BlurShaders>()
|
||||
.expect("Custom Shaders not initialized")
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
type BlurTexture = Mutex<Option<GlesTexture>>;
|
||||
|
||||
struct BlurState {
|
||||
id: Id,
|
||||
renderer_id: Option<ContextId<GlesTexture>>,
|
||||
src: Size<f64, Buffer>,
|
||||
offset: f64,
|
||||
passes: usize,
|
||||
region: Vec<Rectangle<i32, Logical>>,
|
||||
dirty: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
unsafe impl Send for BlurState {}
|
||||
unsafe impl Sync for BlurState {}
|
||||
|
||||
impl BlurState {
|
||||
fn new() -> Self {
|
||||
BlurState {
|
||||
id: Id::new(),
|
||||
renderer_id: None,
|
||||
src: Size::new(0., 0.),
|
||||
offset: 0.,
|
||||
passes: 0,
|
||||
region: Vec::new(),
|
||||
dirty: Arc::new(AtomicBool::new(true)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlurElement {
|
||||
id: Id,
|
||||
src: Size<f64, Buffer>,
|
||||
geometry: Rectangle<f64, Logical>,
|
||||
scaling_shaders: BlurShaders,
|
||||
render_shader: GlesTexProgram,
|
||||
dirty: Arc<AtomicBool>,
|
||||
region: Vec<Rectangle<i32, Physical>>,
|
||||
offset: f64,
|
||||
passes: usize,
|
||||
uniforms: Vec<Uniform<'static>>,
|
||||
}
|
||||
|
||||
impl BlurElement {
|
||||
pub fn from_surface<R: ImportAll + AsGlowRenderer>(
|
||||
renderer: &mut R,
|
||||
states: &SurfaceData,
|
||||
geometry: Rectangle<f64, Logical>,
|
||||
output_scale: f64,
|
||||
radii: [u8; 4],
|
||||
) -> Result<Option<Self>, R::Error> {
|
||||
let mut blur_region_state = states.cached_state.get::<ComputedBlurRegionCachedState>();
|
||||
let Some(region) = blur_region_state.current().blur_region.as_ref() else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let geo = geometry.to_physical_precise_round(output_scale);
|
||||
let mut extended_geo = geo;
|
||||
let radius = OFFSET * 2.0f64.powf(PASSES as f64);
|
||||
extended_geo.loc -= Point::<f64, Physical>::new(radius, radius);
|
||||
extended_geo.size += Size::<f64, Physical>::new(radius, radius).upscale(2.);
|
||||
|
||||
// compute input_to_geo so that it crops the extended capture radius
|
||||
let geo_scale = {
|
||||
let Scale { x, y } = geo.size / extended_geo.size;
|
||||
Matrix3::from_nonuniform_scale(x as f32, y as f32)
|
||||
.invert()
|
||||
.unwrap()
|
||||
};
|
||||
let geo_translation = {
|
||||
let offset = geo.loc - extended_geo.loc;
|
||||
Matrix3::from_translation(Vector2::new(
|
||||
(offset.x / extended_geo.size.w) as f32,
|
||||
(offset.y / extended_geo.size.h) as f32,
|
||||
))
|
||||
.invert()
|
||||
.unwrap()
|
||||
};
|
||||
let input_to_geo = geo_scale * geo_translation;
|
||||
|
||||
let uniforms = vec![
|
||||
Uniform::new("geo_size", (geometry.size.w as f32, geometry.size.h as f32)),
|
||||
Uniform::new(
|
||||
"corner_radius",
|
||||
[
|
||||
radii[3] as f32,
|
||||
radii[1] as f32,
|
||||
radii[0] as f32,
|
||||
radii[2] as f32,
|
||||
],
|
||||
),
|
||||
Uniform::new(
|
||||
"input_to_geo",
|
||||
UniformValue::Matrix3x3 {
|
||||
matrices: vec![*AsRef::<[f32; 9]>::as_ref(&input_to_geo)],
|
||||
transpose: false,
|
||||
},
|
||||
),
|
||||
Uniform::new("noise", UniformValue::_1f(NOISE)),
|
||||
];
|
||||
|
||||
let state = states
|
||||
.data_map
|
||||
.get_or_insert_threadsafe::<Mutex<BlurState>, _>(|| Mutex::new(BlurState::new()));
|
||||
|
||||
Ok(Some(Self::from_state(
|
||||
renderer,
|
||||
extended_geo.to_logical(output_scale),
|
||||
®ion,
|
||||
output_scale,
|
||||
&mut *state.lock().unwrap(),
|
||||
uniforms,
|
||||
)?))
|
||||
}
|
||||
|
||||
fn from_state<R: AsGlowRenderer>(
|
||||
renderer: &mut R,
|
||||
geometry: Rectangle<f64, Logical>,
|
||||
region: &Vec<Rectangle<i32, Logical>>,
|
||||
output_scale: f64,
|
||||
state: &mut BlurState,
|
||||
uniforms: Vec<Uniform<'static>>,
|
||||
) -> Result<Self, R::Error> {
|
||||
let renderer_id = renderer.glow_renderer().context_id();
|
||||
let src = geometry.size.to_buffer(output_scale, Transform::Normal);
|
||||
|
||||
let dirty = !(state
|
||||
.renderer_id
|
||||
.as_ref()
|
||||
.is_some_and(|id| id == &renderer_id)
|
||||
&& &state.region == region
|
||||
&& state.src == src);
|
||||
state.renderer_id = Some(renderer_id);
|
||||
state.offset = OFFSET * output_scale; // we want the blur result to be stable across scaling;
|
||||
state.passes = PASSES;
|
||||
state.region = region.clone();
|
||||
state.src = src;
|
||||
if dirty {
|
||||
state.dirty.store(dirty, Ordering::Release);
|
||||
}
|
||||
|
||||
Ok(BlurElement {
|
||||
id: state.id.clone(),
|
||||
src,
|
||||
geometry,
|
||||
scaling_shaders: BlurShaders::get(renderer),
|
||||
render_shader: ClippingShader::get(renderer),
|
||||
dirty: state.dirty.clone(),
|
||||
offset: state.offset,
|
||||
passes: state.passes,
|
||||
region: region
|
||||
.iter()
|
||||
.map(|rect| rect.to_physical_precise_round(output_scale))
|
||||
.collect(),
|
||||
uniforms,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for BlurElement {
|
||||
fn id(&self) -> &Id {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn current_commit(&self) -> CommitCounter {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn src(&self) -> Rectangle<f64, Buffer> {
|
||||
Rectangle::from_size(self.src)
|
||||
}
|
||||
|
||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||
self.geometry.to_physical_precise_round(scale)
|
||||
}
|
||||
|
||||
fn transform(&self) -> Transform {
|
||||
Transform::Normal
|
||||
}
|
||||
|
||||
fn damage_since(
|
||||
&self,
|
||||
scale: Scale<f64>,
|
||||
commit: Option<CommitCounter>,
|
||||
) -> DamageSet<i32, Physical> {
|
||||
if commit.is_none() || self.dirty.load(Ordering::Acquire) {
|
||||
DamageSet::from_slice(&[self.geometry.to_physical_precise_round(scale)])
|
||||
} else {
|
||||
DamageSet::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn alpha(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
fn kind(&self) -> Kind {
|
||||
Kind::default()
|
||||
}
|
||||
|
||||
fn is_framebuffer_effect(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Renderer + AsGlowRenderer> RenderElement<R> for BlurElement {
|
||||
fn capture_framebuffer(
|
||||
&self,
|
||||
frame: &mut <R>::Frame<'_, '_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
cache: &UserDataMap,
|
||||
) -> Result<(), <R>::Error> {
|
||||
let glow_frame = <R as AsGlowRenderer>::glow_frame_mut(frame);
|
||||
let gles_frame = BorrowMut::<GlesFrame<'_, '_>>::borrow_mut(glow_frame);
|
||||
let transform = gles_frame.transformation();
|
||||
let tex_size = self.src.to_i32_round();
|
||||
|
||||
let texture_ref = cache.get_or_insert_threadsafe(BlurTexture::default);
|
||||
let mut texture_entry = texture_ref.lock().unwrap();
|
||||
if self.dirty.swap(false, Ordering::Acquire) {
|
||||
texture_entry.take();
|
||||
}
|
||||
|
||||
let mut renderer = gles_frame.renderer();
|
||||
if texture_entry.is_none() {
|
||||
*texture_entry = Some(
|
||||
renderer
|
||||
.as_mut()
|
||||
.create_buffer(Fourcc::Abgr8888, tex_size)
|
||||
.map_err(R::from_gles_error)?,
|
||||
);
|
||||
}
|
||||
let texture = texture_entry.as_mut().unwrap();
|
||||
let mut off_texture = renderer
|
||||
.as_mut()
|
||||
.create_buffer(Fourcc::Abgr8888, tex_size)
|
||||
.map_err(R::from_gles_error)?;
|
||||
std::mem::drop(renderer);
|
||||
|
||||
let sync = blit_from_active_fb(gles_frame, src, dst, transform, texture)
|
||||
.map_err(R::from_gles_error)?;
|
||||
gles_frame.wait(&sync).map_err(R::from_gles_error)?;
|
||||
|
||||
let mut textures = [texture, &mut off_texture];
|
||||
render_blur(
|
||||
gles_frame.renderer().as_mut(),
|
||||
&self.scaling_shaders,
|
||||
&mut textures,
|
||||
self.offset,
|
||||
self.passes,
|
||||
)
|
||||
.map_err(R::from_gles_error)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
frame: &mut R::Frame<'_, '_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
damage: &[Rectangle<i32, Physical>],
|
||||
opaque_regions: &[Rectangle<i32, Physical>],
|
||||
cache: Option<&UserDataMap>,
|
||||
) -> Result<(), R::Error> {
|
||||
let glow_frame = <R as AsGlowRenderer>::glow_frame_mut(frame);
|
||||
let damage = self
|
||||
.region
|
||||
.iter()
|
||||
.flat_map(|rect| damage.iter().flat_map(|r| r.intersection(*rect)))
|
||||
.collect::<Vec<_>>();
|
||||
let cache = cache.expect("Framebuffer element without cache?");
|
||||
let Some(texture) = cache.get::<BlurTexture>() else {
|
||||
return Err(R::from_gles_error(GlesError::BlitError));
|
||||
};
|
||||
let texture_ref = texture.lock().unwrap();
|
||||
|
||||
BorrowMut::<GlesFrame<'_, '_>>::borrow_mut(glow_frame)
|
||||
.render_texture_from_to(
|
||||
texture_ref
|
||||
.as_ref()
|
||||
.ok_or(R::from_gles_error(GlesError::BlitError))?,
|
||||
src,
|
||||
dst,
|
||||
&damage,
|
||||
opaque_regions,
|
||||
Transform::Normal,
|
||||
1.0,
|
||||
Some(&self.render_shader),
|
||||
&self.uniforms,
|
||||
)
|
||||
.map_err(R::from_gles_error)
|
||||
}
|
||||
}
|
||||
|
||||
fn blit_from_active_fb(
|
||||
frame: &mut GlesFrame<'_, '_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
transform: Transform,
|
||||
to_texture: &mut GlesTexture,
|
||||
) -> Result<SyncPoint, GlesError> {
|
||||
if transform != Transform::Normal {
|
||||
let tex_size = to_texture
|
||||
.size()
|
||||
.to_logical(1, Transform::Normal)
|
||||
.to_physical(1);
|
||||
let mut renderer = frame.renderer();
|
||||
let mut tmp_texture = renderer.as_mut().create_buffer(
|
||||
Fourcc::Abgr8888,
|
||||
dst.size.to_logical(1).to_buffer(1, Transform::Normal),
|
||||
)?;
|
||||
let mut fb_tmp = renderer.as_mut().bind(&mut tmp_texture)?;
|
||||
let mut fb = renderer.as_mut().bind(to_texture)?;
|
||||
std::mem::drop(renderer);
|
||||
|
||||
frame.blit_to(
|
||||
&mut fb_tmp,
|
||||
dst,
|
||||
Rectangle::from_size(dst.size),
|
||||
TextureFilter::Linear,
|
||||
)?;
|
||||
std::mem::drop(fb_tmp);
|
||||
|
||||
let mut renderer = frame.renderer();
|
||||
let mut frame = renderer
|
||||
.as_mut()
|
||||
.render(&mut fb, tex_size, Transform::Normal)?;
|
||||
Frame::render_texture_from_to(
|
||||
&mut frame,
|
||||
&tmp_texture,
|
||||
Rectangle::from_size(
|
||||
dst.size
|
||||
.to_logical(1)
|
||||
.to_buffer(1, Transform::Normal)
|
||||
.to_f64(),
|
||||
),
|
||||
src.to_logical(1., Transform::Normal, &src.size)
|
||||
.to_physical(1.)
|
||||
.to_i32_round(),
|
||||
&[Rectangle::from_size(dst.size)],
|
||||
&[Rectangle::from_size(dst.size)],
|
||||
transform,
|
||||
1.0,
|
||||
)?;
|
||||
std::mem::drop(tmp_texture);
|
||||
frame.finish()
|
||||
} else {
|
||||
let mut fb = frame.renderer().as_mut().bind(to_texture)?;
|
||||
|
||||
frame
|
||||
.blit_to(
|
||||
&mut fb,
|
||||
dst,
|
||||
src.to_logical(1., Transform::Normal, &src.size)
|
||||
.to_physical(1.)
|
||||
.to_i32_round(),
|
||||
TextureFilter::Linear,
|
||||
)
|
||||
.map(|_| SyncPoint::signaled())
|
||||
}
|
||||
}
|
||||
|
||||
fn render_blur(
|
||||
renderer: &mut GlesRenderer,
|
||||
shaders: &BlurShaders,
|
||||
textures: &mut [&mut GlesTexture; 2],
|
||||
offset: f64,
|
||||
passes: usize,
|
||||
) -> Result<(), GlesError> {
|
||||
for i in 0..passes {
|
||||
let tex_size = textures[0].size();
|
||||
let [src_tex, target_tex] = textures;
|
||||
let mut fb = renderer.bind(*target_tex)?;
|
||||
|
||||
let adjusted_tex_size = tex_size.downscale(1 << i);
|
||||
let target_tex_size = tex_size
|
||||
.downscale(1 << i + 1)
|
||||
.to_logical(1, Transform::Normal)
|
||||
.to_physical(1);
|
||||
let half_pixel = [
|
||||
0.5 / (adjusted_tex_size.w as f32),
|
||||
0.5 / (adjusted_tex_size.h as f32),
|
||||
];
|
||||
|
||||
let mut frame = renderer.render(
|
||||
&mut fb,
|
||||
tex_size.to_logical(1, Transform::Normal).to_physical(1),
|
||||
Transform::Normal,
|
||||
)?;
|
||||
frame.clear(
|
||||
Color32F::new(0., 0., 0., 0.),
|
||||
&[Rectangle::from_size(
|
||||
tex_size.to_logical(1, Transform::Normal).to_physical(1),
|
||||
)],
|
||||
)?;
|
||||
frame.with_context(|gl| unsafe {
|
||||
gl.TexParameteri(
|
||||
ffi::TEXTURE_2D,
|
||||
ffi::TEXTURE_WRAP_S,
|
||||
ffi::CLAMP_TO_EDGE as i32,
|
||||
);
|
||||
gl.TexParameteri(
|
||||
ffi::TEXTURE_2D,
|
||||
ffi::TEXTURE_WRAP_T,
|
||||
ffi::CLAMP_TO_EDGE as i32,
|
||||
);
|
||||
})?;
|
||||
frame.render_texture_from_to(
|
||||
src_tex,
|
||||
Rectangle::from_size(adjusted_tex_size.to_f64()),
|
||||
Rectangle::from_size(target_tex_size),
|
||||
&[Rectangle::from_size(target_tex_size)],
|
||||
&[Rectangle::from_size(target_tex_size)],
|
||||
Transform::Normal,
|
||||
1.0,
|
||||
Some(&shaders.down),
|
||||
&[
|
||||
Uniform::new("half_pixel", half_pixel),
|
||||
Uniform::new("offset", (offset / (1 << i) as f64) as f32),
|
||||
],
|
||||
)?;
|
||||
frame.with_context(|gl| unsafe {
|
||||
gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::REPEAT as i32);
|
||||
gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::REPEAT as i32);
|
||||
})?;
|
||||
let sync = frame.finish()?;
|
||||
std::mem::drop(fb);
|
||||
renderer.wait(&sync)?;
|
||||
|
||||
textures.swap(0, 1);
|
||||
}
|
||||
|
||||
for i in 0..passes {
|
||||
let tex_size = textures[0].size();
|
||||
let [src_tex, target_tex] = textures;
|
||||
let mut fb = renderer.bind(*target_tex)?;
|
||||
|
||||
let adjusted_tex_size = tex_size.downscale(1 << (passes - i));
|
||||
let target_tex_size = tex_size
|
||||
.downscale(1 << (passes - i - 1))
|
||||
.to_logical(1, Transform::Normal)
|
||||
.to_physical(1);
|
||||
let half_pixel = [
|
||||
0.5 / (adjusted_tex_size.w as f32),
|
||||
0.5 / (adjusted_tex_size.h as f32),
|
||||
];
|
||||
|
||||
let mut frame = renderer.render(
|
||||
&mut fb,
|
||||
tex_size.to_logical(1, Transform::Normal).to_physical(1),
|
||||
Transform::Normal,
|
||||
)?;
|
||||
frame.clear(
|
||||
Color32F::new(0., 0., 0., 0.),
|
||||
&[Rectangle::from_size(
|
||||
tex_size.to_logical(1, Transform::Normal).to_physical(1),
|
||||
)],
|
||||
)?;
|
||||
frame.with_context(|gl| unsafe {
|
||||
gl.TexParameteri(
|
||||
ffi::TEXTURE_2D,
|
||||
ffi::TEXTURE_WRAP_S,
|
||||
ffi::CLAMP_TO_EDGE as i32,
|
||||
);
|
||||
gl.TexParameteri(
|
||||
ffi::TEXTURE_2D,
|
||||
ffi::TEXTURE_WRAP_T,
|
||||
ffi::CLAMP_TO_EDGE as i32,
|
||||
);
|
||||
})?;
|
||||
frame.render_texture_from_to(
|
||||
src_tex,
|
||||
Rectangle::from_size(adjusted_tex_size.to_f64()),
|
||||
Rectangle::from_size(target_tex_size),
|
||||
&[Rectangle::from_size(target_tex_size)],
|
||||
&[Rectangle::from_size(target_tex_size)],
|
||||
Transform::Normal,
|
||||
1.0,
|
||||
Some(&shaders.up),
|
||||
&[
|
||||
Uniform::new("half_pixel", half_pixel),
|
||||
Uniform::new("offset", (offset / (1 << (passes - i)) as f64) as f32),
|
||||
],
|
||||
)?;
|
||||
frame.with_context(|gl| unsafe {
|
||||
gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::REPEAT as i32);
|
||||
gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::REPEAT as i32);
|
||||
})?;
|
||||
let sync = frame.finish()?;
|
||||
std::mem::drop(fb);
|
||||
renderer.wait(&sync)?;
|
||||
|
||||
textures.swap(0, 1);
|
||||
}
|
||||
|
||||
// textures always end up the right way around with `self.texture` containing our final render,
|
||||
// since we render PASSES * 2 (downscale and upscale), so the number of swaps is always even.
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -111,6 +111,7 @@ where
|
|||
transpose: false,
|
||||
},
|
||||
),
|
||||
Uniform::new("noise", UniformValue::_1f(0.0)),
|
||||
];
|
||||
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -12,13 +12,16 @@ use smithay::{
|
|||
use tracing::warn;
|
||||
|
||||
use crate::backend::render::{
|
||||
element::AsGlowRenderer, wayland::clipped_surface::ClippedSurfaceRenderElement,
|
||||
element::AsGlowRenderer,
|
||||
wayland::{blur_effect::BlurElement, clipped_surface::ClippedSurfaceRenderElement},
|
||||
};
|
||||
|
||||
pub mod blur_effect;
|
||||
pub mod clipped_surface;
|
||||
|
||||
render_elements! {
|
||||
pub SurfaceRenderElement<R> where R: AsGlowRenderer + ImportAll;
|
||||
Blur=BlurElement,
|
||||
Clipped=ClippedSurfaceRenderElement<R>,
|
||||
Wayland=WaylandSurfaceRenderElement<R>,
|
||||
}
|
||||
|
|
@ -67,6 +70,7 @@ pub fn push_render_elements_from_surface_tree<R>(
|
|||
let mut location = *location;
|
||||
let kind = kind.eval(states);
|
||||
let data = states.data_map.get::<RendererSurfaceStateUserData>();
|
||||
let mut blur = Ok(None);
|
||||
|
||||
if let Some(data) = data {
|
||||
let has_view = if let Some(view) = data.lock().unwrap().view() {
|
||||
|
|
@ -82,6 +86,9 @@ pub fn push_render_elements_from_surface_tree<R>(
|
|||
renderer, surface, states, location, alpha, kind,
|
||||
) {
|
||||
Ok(Some(surface)) => {
|
||||
blur = BlurElement::from_surface(
|
||||
renderer, states, geometry, scale.x, radii,
|
||||
);
|
||||
let elem: SurfaceRenderElement<R> = if radii.iter().any(|r| *r != 0)
|
||||
&& should_clip
|
||||
&& ClippedSurfaceRenderElement::will_clip(
|
||||
|
|
@ -113,6 +120,16 @@ pub fn push_render_elements_from_surface_tree<R>(
|
|||
if surface == main_surface {
|
||||
passed_main = true;
|
||||
}
|
||||
|
||||
if let Ok(Some(elem)) = blur {
|
||||
if let Some(push_below) = push_below.as_mut()
|
||||
&& passed_main
|
||||
{
|
||||
push_below(elem.into());
|
||||
} else {
|
||||
push_above(elem.into());
|
||||
}
|
||||
}
|
||||
},
|
||||
|_, _, _| true,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ use smithay::{
|
|||
utils::{Clock, Monotonic, Point},
|
||||
wayland::{
|
||||
alpha_modifier::AlphaModifierState,
|
||||
background_effect::BackgroundEffectState,
|
||||
compositor::{CompositorClientState, CompositorState, SurfaceData},
|
||||
cursor_shape::CursorShapeManagerState,
|
||||
dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState},
|
||||
|
|
@ -281,6 +282,7 @@ pub struct Common {
|
|||
pub a11y_state: A11yState,
|
||||
pub dbus_state: DBusState,
|
||||
pub keyboard_layout_state: KeyboardLayoutState,
|
||||
pub background_effect_state: BackgroundEffectState,
|
||||
|
||||
// shell-related wayland state
|
||||
pub xdg_shell_state: XdgShellState,
|
||||
|
|
@ -686,6 +688,8 @@ impl State {
|
|||
FixesState::new::<Self>(dh);
|
||||
let keyboard_layout_state = KeyboardLayoutState::new::<State, _>(&dh, client_not_sandboxed);
|
||||
|
||||
let background_effect_state = BackgroundEffectState::new::<Self>(dh);
|
||||
|
||||
let idle_notifier_state = IdleNotifierState::<Self>::new(dh, handle.clone());
|
||||
let idle_inhibit_manager_state = IdleInhibitManagerState::new::<State>(dh);
|
||||
let idle_inhibiting_surfaces = HashSet::new();
|
||||
|
|
@ -789,6 +793,7 @@ impl State {
|
|||
xdg_activation_state,
|
||||
xdg_foreign_state,
|
||||
workspace_state,
|
||||
background_effect_state,
|
||||
a11y_state,
|
||||
xwayland_scale: None,
|
||||
xwayland_state: None,
|
||||
|
|
|
|||
59
src/wayland/handlers/background_effect.rs
Normal file
59
src/wayland/handlers/background_effect.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use smithay::{
|
||||
reexports::wayland_server::{DisplayHandle, protocol::wl_surface::WlSurface},
|
||||
utils::{Logical, Rectangle},
|
||||
wayland::{
|
||||
background_effect::{Capability, ExtBackgroundEffectHandler},
|
||||
compositor::{Cacheable, RectangleKind, RegionAttributes, with_states},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ComputedBlurRegionCachedState {
|
||||
/// Region of the surface that will have its background blurred.
|
||||
pub blur_region: Option<Vec<Rectangle<i32, Logical>>>,
|
||||
}
|
||||
|
||||
impl Cacheable for ComputedBlurRegionCachedState {
|
||||
fn commit(&mut self, _dh: &DisplayHandle) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
|
||||
fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) {
|
||||
*into = self;
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtBackgroundEffectHandler for State {
|
||||
fn capabilities(&self) -> Capability {
|
||||
Capability::Blur
|
||||
}
|
||||
|
||||
fn set_blur_region(&mut self, surface: WlSurface, region: RegionAttributes) {
|
||||
with_states(&surface, |states| {
|
||||
let mut blur_state = states.cached_state.get::<ComputedBlurRegionCachedState>();
|
||||
|
||||
blur_state.pending().blur_region = Some({
|
||||
let (added, subtracted) = region
|
||||
.rects
|
||||
.iter()
|
||||
.cloned()
|
||||
.partition::<Vec<_>, _>(|(op, _)| matches!(op, RectangleKind::Add));
|
||||
let added = added.into_iter().map(|(_, rect)| rect).collect::<Vec<_>>();
|
||||
Rectangle::subtract_rects_many_in_place(
|
||||
added,
|
||||
subtracted.into_iter().map(|(_, rect)| rect),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn unset_blur_region(&mut self, surface: WlSurface) {
|
||||
with_states(&surface, |states| {
|
||||
let mut blur_state = states.cached_state.get::<ComputedBlurRegionCachedState>();
|
||||
|
||||
blur_state.pending().blur_region.take();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod a11y;
|
||||
pub mod background_effect;
|
||||
pub mod buffer;
|
||||
pub mod compositor;
|
||||
pub mod corner_radius;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue