diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-04-21 20:10:35 +0400 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-04-21 20:16:54 +0400 |
| commit | 49f5402669012be33f8cd111311c3e39fd4751c0 (patch) | |
| tree | a90eab10775b63cddb0772923d43875ee13ef8b6 /src | |
| parent | 2ecbb3f6f8ceacdccc66a597e372c70029325dbf (diff) | |
| download | niri-49f5402669012be33f8cd111311c3e39fd4751c0.tar.gz niri-49f5402669012be33f8cd111311c3e39fd4751c0.tar.bz2 niri-49f5402669012be33f8cd111311c3e39fd4751c0.zip | |
Implement window-resize custom-shader
Diffstat (limited to 'src')
| -rw-r--r-- | src/backend/tty.rs | 11 | ||||
| -rw-r--r-- | src/backend/winit.rs | 6 | ||||
| -rw-r--r-- | src/layout/mod.rs | 2 | ||||
| -rw-r--r-- | src/layout/tile.rs | 31 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 8 | ||||
| -rw-r--r-- | src/main.rs | 4 | ||||
| -rw-r--r-- | src/niri.rs | 13 | ||||
| -rw-r--r-- | src/render_helpers/crossfade.rs | 161 | ||||
| -rw-r--r-- | src/render_helpers/mod.rs | 2 | ||||
| -rw-r--r-- | src/render_helpers/primary_gpu_pixel_shader_with_textures.rs | 6 | ||||
| -rw-r--r-- | src/render_helpers/resize.rs | 199 | ||||
| -rw-r--r-- | src/render_helpers/shaders/crossfade.frag | 31 | ||||
| -rw-r--r-- | src/render_helpers/shaders/mod.rs | 75 | ||||
| -rw-r--r-- | src/render_helpers/shaders/resize.frag | 41 |
14 files changed, 362 insertions, 228 deletions
diff --git a/src/backend/tty.rs b/src/backend/tty.rs index fd6cf0d6..6a5b3445 100644 --- a/src/backend/tty.rs +++ b/src/backend/tty.rs @@ -491,8 +491,15 @@ impl Tty { warn!("error binding wl-display in EGL: {err:?}"); } - resources::init(renderer.as_gles_renderer()); - shaders::init(renderer.as_gles_renderer()); + let gles_renderer = renderer.as_gles_renderer(); + resources::init(gles_renderer); + shaders::init(gles_renderer); + + let config = self.config.borrow(); + if let Some(src) = config.animations.window_resize.custom_shader.as_deref() { + shaders::set_custom_resize_program(gles_renderer, Some(src)); + } + drop(config); // Create the dmabuf global. let primary_formats = renderer.dmabuf_formats().collect::<HashSet<_>>(); diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 177dab85..1d1dbc8a 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -135,6 +135,12 @@ impl Winit { resources::init(renderer); shaders::init(renderer); + let config = self.config.borrow(); + if let Some(src) = config.animations.window_resize.custom_shader.as_deref() { + shaders::set_custom_resize_program(renderer, Some(src)); + } + drop(config); + niri.add_output(self.output.clone(), None, false); } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 68b592db..4c570ef2 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -238,7 +238,7 @@ impl Options { center_focused_column: layout.center_focused_column, preset_widths, default_width, - animations: config.animations, + animations: config.animations.clone(), } } } diff --git a/src/layout/tile.rs b/src/layout/tile.rs index 1d639828..ed6773da 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -16,10 +16,10 @@ use super::{ }; use crate::animation::Animation; use crate::niri_render_elements; -use crate::render_helpers::crossfade::CrossfadeRenderElement; use crate::render_helpers::offscreen::OffscreenRenderElement; use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement; use crate::render_helpers::renderer::NiriRenderer; +use crate::render_helpers::resize::ResizeRenderElement; use crate::render_helpers::shaders::Shaders; use crate::render_helpers::snapshot::RenderSnapshot; use crate::render_helpers::{render_to_encompassing_texture, RenderTarget, ToRenderElement}; @@ -73,7 +73,7 @@ niri_render_elements! { FocusRing = FocusRingRenderElement, SolidColor = SolidColorRenderElement, Offscreen = RescaleRenderElement<OffscreenRenderElement>, - Crossfade = CrossfadeRenderElement, + Resize = ResizeRenderElement, } } @@ -154,7 +154,7 @@ impl<W: LayoutElement> Tile<W> { let change = self.window.size().to_point() - size_from.to_point(); let change = max(change.x.abs(), change.y.abs()); if change > RESIZE_ANIMATION_THRESHOLD { - let anim = Animation::new(0., 1., 0., self.options.animations.window_resize.0); + let anim = Animation::new(0., 1., 0., self.options.animations.window_resize.anim); self.resize_animation = Some(ResizeAnimation { anim, size_from, @@ -527,12 +527,12 @@ impl<W: LayoutElement> Tile<W> { let gles_renderer = renderer.as_gles_renderer(); - // If we're resizing, try to render a crossfade, or a fallback. - let mut crossfade = None; - let mut crossfade_fallback = None; + // If we're resizing, try to render a shader, or a fallback. + let mut resize_shader = None; + let mut resize_fallback = None; if let Some(resize) = &self.resize_animation { - if Shaders::get(gles_renderer).crossfade.is_some() { + if Shaders::get(gles_renderer).resize().is_some() { if let Some(texture_from) = resize.snapshot.texture(gles_renderer, scale, target) { let window_elements = self.window @@ -548,7 +548,7 @@ impl<W: LayoutElement> Tile<W> { .ok(); if let Some((texture_current, _sync_point, texture_current_geo)) = current { - let elem = CrossfadeRenderElement::new( + let elem = ResizeRenderElement::new( gles_renderer, area, scale, @@ -556,20 +556,21 @@ impl<W: LayoutElement> Tile<W> { resize.snapshot.size, (texture_current, texture_current_geo), window_size, + resize.anim.value() as f32, resize.anim.clamped_value().clamp(0., 1.) as f32, alpha, ) - .expect("we checked the crossfade shader above"); + .expect("we checked the resize shader above"); self.window .set_offscreen_element_id(Some(elem.id().clone())); - crossfade = Some(elem.into()); + resize_shader = Some(elem.into()); } } } - if crossfade.is_none() { + if resize_shader.is_none() { let fallback_buffer = SolidColorBuffer::new(area.size, [1., 0., 0., 1.]); - crossfade_fallback = Some( + resize_fallback = Some( SolidColorRenderElement::from_buffer( &fallback_buffer, area.loc.to_physical_precise_round(scale), @@ -585,7 +586,7 @@ impl<W: LayoutElement> Tile<W> { // If we're not resizing, render the window itself. let mut window = None; - if crossfade.is_none() && crossfade_fallback.is_none() { + if resize_shader.is_none() && resize_fallback.is_none() { window = Some( self.window .render(renderer, window_render_loc, scale, alpha, target) @@ -594,9 +595,9 @@ impl<W: LayoutElement> Tile<W> { ); } - let rv = crossfade + let rv = resize_shader .into_iter() - .chain(crossfade_fallback) + .chain(resize_fallback) .chain(window.into_iter().flatten()); let elem = self.effective_border_width().map(|width| { diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 116f3759..b6b38285 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -1118,14 +1118,14 @@ impl<W: LayoutElement> Workspace<W> { for col in &mut self.columns[col_idx + 1..] { col.animate_move_from_with_config( offset, - self.options.animations.window_resize.0, + self.options.animations.window_resize.anim, ); } } else { for col in &mut self.columns[..=col_idx] { col.animate_move_from_with_config( -offset, - self.options.animations.window_resize.0, + self.options.animations.window_resize.anim, ); } } @@ -1148,7 +1148,7 @@ impl<W: LayoutElement> Workspace<W> { // Synchronize the horizontal view movement with the resize so that it looks nice. This // is especially important for always-centered view. let config = if started_resize_anim { - self.options.animations.window_resize.0 + self.options.animations.window_resize.anim } else { self.options.animations.horizontal_view_movement.0 }; @@ -2356,7 +2356,7 @@ impl<W: LayoutElement> Column<W> { for tile in &mut self.tiles[tile_idx + 1..] { tile.animate_move_y_from_with_config( offset, - self.options.animations.window_resize.0, + self.options.animations.window_resize.anim, ); } } diff --git a/src/main.rs b/src/main.rs index 58e2c766..f117769d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,8 @@ use smithay::reexports::calloop::EventLoop; use smithay::reexports::wayland_server::Display; use tracing_subscriber::EnvFilter; +const DEFAULT_LOG_FILTER: &str = "niri=debug,smithay::backend::renderer::gles=error"; + fn main() -> Result<(), Box<dyn std::error::Error>> { // Set backtrace defaults if not set. if env::var_os("RUST_BACKTRACE").is_none() { @@ -50,7 +52,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { ); } - let directives = env::var("RUST_LOG").unwrap_or_else(|_| "niri=debug".to_owned()); + let directives = env::var("RUST_LOG").unwrap_or_else(|_| DEFAULT_LOG_FILTER.to_owned()); let env_filter = EnvFilter::builder().parse_lossy(directives); tracing_subscriber::fmt() .compact() diff --git a/src/niri.rs b/src/niri.rs index 145d143e..861e53f1 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -107,7 +107,9 @@ use crate::protocols::gamma_control::GammaControlManagerState; use crate::protocols::screencopy::{Screencopy, ScreencopyManagerState}; use crate::pw_utils::{Cast, PipeWire}; use crate::render_helpers::renderer::NiriRenderer; -use crate::render_helpers::{render_to_shm, render_to_texture, render_to_vec, RenderTarget}; +use crate::render_helpers::{ + render_to_shm, render_to_texture, render_to_vec, shaders, RenderTarget, +}; use crate::scroll_tracker::ScrollTracker; use crate::ui::config_error_notification::ConfigErrorNotification; use crate::ui::exit_confirm_dialog::ExitConfirmDialog; @@ -895,6 +897,15 @@ impl State { window_rules_changed = true; } + if config.animations.window_resize.custom_shader + != old_config.animations.window_resize.custom_shader + { + let src = config.animations.window_resize.custom_shader.as_deref(); + self.backend.with_primary_renderer(|renderer| { + shaders::set_custom_resize_program(renderer, src); + }); + } + *old_config = config; // Release the borrow. diff --git a/src/render_helpers/crossfade.rs b/src/render_helpers/crossfade.rs deleted file mode 100644 index 78e56ba4..00000000 --- a/src/render_helpers/crossfade.rs +++ /dev/null @@ -1,161 +0,0 @@ -use std::collections::HashMap; - -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}; -use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Size, Transform}; - -use super::primary_gpu_pixel_shader_with_textures::PrimaryGpuPixelShaderWithTexturesRenderElement; -use super::renderer::AsGlesFrame; -use super::shaders::Shaders; -use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError}; - -#[derive(Debug)] -pub struct CrossfadeRenderElement(PrimaryGpuPixelShaderWithTexturesRenderElement); - -impl CrossfadeRenderElement { - #[allow(clippy::too_many_arguments)] - pub fn new( - renderer: &mut GlesRenderer, - area: Rectangle<i32, Logical>, - scale: Scale<f64>, - texture_from: (GlesTexture, Rectangle<i32, Physical>), - size_from: Size<i32, Logical>, - texture_to: (GlesTexture, Rectangle<i32, Physical>), - size_to: Size<i32, Logical>, - amount: f32, - result_alpha: f32, - ) -> Option<Self> { - let (texture_from, texture_from_geo) = texture_from; - let (texture_to, texture_to_geo) = texture_to; - - let scale_from = area.size.to_f64() / size_from.to_f64(); - let scale_to = area.size.to_f64() / size_to.to_f64(); - - let tex_from_geo = texture_from_geo.to_f64().upscale(scale_from); - let tex_to_geo = texture_to_geo.to_f64().upscale(scale_to); - let combined_geo = tex_from_geo.merge(tex_to_geo); - - let size = combined_geo - .size - .to_logical(1.) - .to_buffer(1., Transform::Normal); - - let area = Rectangle::from_loc_and_size( - area.loc + combined_geo.loc.to_logical(scale).to_i32_round(), - combined_geo.size.to_logical(scale).to_i32_round(), - ); - - let tex_from_loc = (tex_from_geo.loc - combined_geo.loc) - .downscale((combined_geo.size.w, combined_geo.size.h)); - let tex_to_loc = (tex_to_geo.loc - combined_geo.loc) - .downscale((combined_geo.size.w, combined_geo.size.h)); - let tex_from_size = tex_from_geo.size / combined_geo.size; - let tex_to_size = tex_to_geo.size / combined_geo.size; - - Shaders::get(renderer).crossfade.clone().map(|shader| { - Self(PrimaryGpuPixelShaderWithTexturesRenderElement::new( - shader, - HashMap::from([ - (String::from("tex_from"), texture_from), - (String::from("tex_to"), texture_to), - ]), - area, - size, - None, - result_alpha, - vec![ - Uniform::new( - "tex_from_loc", - (tex_from_loc.x as f32, tex_from_loc.y as f32), - ), - Uniform::new( - "tex_from_size", - (tex_from_size.x as f32, tex_from_size.y as f32), - ), - Uniform::new("tex_to_loc", (tex_to_loc.x as f32, tex_to_loc.y as f32)), - Uniform::new("tex_to_size", (tex_to_size.x as f32, tex_to_size.y as f32)), - Uniform::new("amount", amount), - ], - Kind::Unspecified, - )) - }) - } -} - -impl Element for CrossfadeRenderElement { - fn id(&self) -> &Id { - self.0.id() - } - - fn current_commit(&self) -> CommitCounter { - self.0.current_commit() - } - - fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> { - self.0.geometry(scale) - } - - fn transform(&self) -> Transform { - self.0.transform() - } - - fn src(&self) -> Rectangle<f64, Buffer> { - self.0.src() - } - - fn damage_since( - &self, - scale: Scale<f64>, - commit: Option<CommitCounter>, - ) -> DamageSet<i32, Physical> { - self.0.damage_since(scale, commit) - } - - fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> { - self.0.opaque_regions(scale) - } - - fn alpha(&self) -> f32 { - self.0.alpha() - } - - fn kind(&self) -> Kind { - self.0.kind() - } -} - -impl RenderElement<GlesRenderer> for CrossfadeRenderElement { - fn draw( - &self, - frame: &mut GlesFrame<'_>, - src: Rectangle<f64, Buffer>, - dst: Rectangle<i32, Physical>, - damage: &[Rectangle<i32, Physical>], - ) -> Result<(), GlesError> { - RenderElement::<GlesRenderer>::draw(&self.0, frame, src, dst, damage)?; - Ok(()) - } - - fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> { - self.0.underlying_storage(renderer) - } -} - -impl<'render> RenderElement<TtyRenderer<'render>> for CrossfadeRenderElement { - fn draw( - &self, - frame: &mut TtyFrame<'_, '_>, - src: Rectangle<f64, Buffer>, - dst: Rectangle<i32, Physical>, - damage: &[Rectangle<i32, Physical>], - ) -> Result<(), TtyRendererError<'render>> { - let gles_frame = frame.as_gles_frame(); - RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage)?; - Ok(()) - } - - fn underlying_storage(&self, renderer: &mut TtyRenderer<'render>) -> Option<UnderlyingStorage> { - self.0.underlying_storage(renderer) - } -} diff --git a/src/render_helpers/mod.rs b/src/render_helpers/mod.rs index de40c274..0be48bc6 100644 --- a/src/render_helpers/mod.rs +++ b/src/render_helpers/mod.rs @@ -16,7 +16,6 @@ use smithay::wayland::shm; use self::primary_gpu_texture::PrimaryGpuTextureRenderElement; -pub mod crossfade; pub mod gradient; pub mod offscreen; pub mod primary_gpu_pixel_shader; @@ -24,6 +23,7 @@ pub mod primary_gpu_pixel_shader_with_textures; pub mod primary_gpu_texture; pub mod render_elements; pub mod renderer; +pub mod resize; pub mod resources; pub mod shaders; pub mod snapshot; diff --git a/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs b/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs index 3037d731..45f93342 100644 --- a/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs +++ b/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs @@ -120,6 +120,12 @@ impl PixelWithTexturesProgram { compile_program(gl, src, additional_uniforms, texture_uniforms) })? } + + pub fn destroy(self, renderer: &mut GlesRenderer) -> Result<(), GlesError> { + renderer.with_context(move |gl| unsafe { + gl.DeleteProgram(self.0.program); + }) + } } impl PrimaryGpuPixelShaderWithTexturesRenderElement { diff --git a/src/render_helpers/resize.rs b/src/render_helpers/resize.rs new file mode 100644 index 00000000..2242c99e --- /dev/null +++ b/src/render_helpers/resize.rs @@ -0,0 +1,199 @@ +use std::collections::HashMap; + +use glam::{Mat3, Vec2}; +use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage}; +use smithay::backend::renderer::gles::{ + GlesError, GlesFrame, GlesRenderer, GlesTexture, Uniform, UniformValue, +}; +use smithay::backend::renderer::utils::{CommitCounter, DamageSet}; +use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Size, Transform}; + +use super::primary_gpu_pixel_shader_with_textures::PrimaryGpuPixelShaderWithTexturesRenderElement; +use super::renderer::AsGlesFrame; +use super::shaders::Shaders; +use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError}; + +#[derive(Debug)] +pub struct ResizeRenderElement(PrimaryGpuPixelShaderWithTexturesRenderElement); + +impl ResizeRenderElement { + #[allow(clippy::too_many_arguments)] + pub fn new( + renderer: &mut GlesRenderer, + area: Rectangle<i32, Logical>, + scale: Scale<f64>, + texture_prev: (GlesTexture, Rectangle<i32, Physical>), + size_prev: Size<i32, Logical>, + texture_next: (GlesTexture, Rectangle<i32, Physical>), + size_next: Size<i32, Logical>, + progress: f32, + clamped_progress: f32, + result_alpha: f32, + ) -> Option<Self> { + let curr_geo = area; + + let (texture_prev, tex_prev_geo) = texture_prev; + let (texture_next, tex_next_geo) = texture_next; + + let scale_prev = area.size.to_f64() / size_prev.to_f64(); + let scale_next = area.size.to_f64() / size_next.to_f64(); + + // Compute the area necessary to fit a crossfade. + let tex_prev_geo_scaled = tex_prev_geo.to_f64().upscale(scale_prev); + let tex_next_geo_scaled = tex_next_geo.to_f64().upscale(scale_next); + let combined_geo = tex_prev_geo_scaled.merge(tex_next_geo_scaled); + + let size = combined_geo + .size + .to_logical(1.) + .to_buffer(1., Transform::Normal); + + let area = Rectangle::from_loc_and_size( + area.loc + combined_geo.loc.to_logical(scale).to_i32_round(), + combined_geo.size.to_logical(scale).to_i32_round(), + ); + + // Convert Smithay types into glam types. + let area_loc = Vec2::new(area.loc.x as f32, area.loc.y as f32); + let area_size = Vec2::new(area.size.w as f32, area.size.h as f32); + + let curr_geo_loc = Vec2::new(curr_geo.loc.x as f32, curr_geo.loc.y as f32); + let curr_geo_size = Vec2::new(curr_geo.size.w as f32, curr_geo.size.h as f32); + + let tex_prev_geo_loc = Vec2::new(tex_prev_geo.loc.x as f32, tex_prev_geo.loc.y as f32); + let tex_prev_geo_size = Vec2::new(tex_prev_geo.size.w as f32, tex_prev_geo.size.h as f32); + + let tex_next_geo_loc = Vec2::new(tex_next_geo.loc.x as f32, tex_next_geo.loc.y as f32); + let tex_next_geo_size = Vec2::new(tex_next_geo.size.w as f32, tex_next_geo.size.h as f32); + + let size_prev = Vec2::new(size_prev.w as f32, size_prev.h as f32); + let size_next = Vec2::new(size_next.w as f32, size_next.h as f32); + + let scale = Vec2::new(scale.x as f32, scale.y as f32); + + // Compute the transformation matrices. + let input_to_curr_geo = Mat3::from_scale(area_size / curr_geo_size) + * Mat3::from_translation((area_loc - curr_geo_loc) / area_size); + let input_to_prev_geo = Mat3::from_scale(area_size / size_prev) + * Mat3::from_translation((area_loc - curr_geo_loc) / area_size); + let input_to_next_geo = Mat3::from_scale(area_size / size_next) + * Mat3::from_translation((area_loc - curr_geo_loc) / area_size); + + let geo_to_tex_prev = Mat3::from_translation(-tex_prev_geo_loc / tex_prev_geo_size) + * Mat3::from_scale(size_prev / tex_prev_geo_size * scale); + let geo_to_tex_next = Mat3::from_translation(-tex_next_geo_loc / tex_next_geo_size) + * Mat3::from_scale(size_next / tex_next_geo_size * scale); + + // Create the shader. + let make_uniform = |name, mat: Mat3| { + Uniform::new( + name, + UniformValue::Matrix3x3 { + matrices: vec![mat.to_cols_array()], + transpose: false, + }, + ) + }; + + Shaders::get(renderer).resize().map(|shader| { + Self(PrimaryGpuPixelShaderWithTexturesRenderElement::new( + shader, + HashMap::from([ + (String::from("tex_prev"), texture_prev), + (String::from("tex_next"), texture_next), + ]), + area, + size, + None, + result_alpha, + vec![ + make_uniform("input_to_curr_geo", input_to_curr_geo), + make_uniform("input_to_prev_geo", input_to_prev_geo), + make_uniform("input_to_next_geo", input_to_next_geo), + make_uniform("geo_to_tex_prev", geo_to_tex_prev), + make_uniform("geo_to_tex_next", geo_to_tex_next), + Uniform::new("progress", progress), + Uniform::new("clamped_progress", clamped_progress), + ], + Kind::Unspecified, + )) + }) + } +} + +impl Element for ResizeRenderElement { + fn id(&self) -> &Id { + self.0.id() + } + + fn current_commit(&self) -> CommitCounter { + self.0.current_commit() + } + + fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> { + self.0.geometry(scale) + } + + fn transform(&self) -> Transform { + self.0.transform() + } + + fn src(&self) -> Rectangle<f64, Buffer> { + self.0.src() + } + + fn damage_since( + &self, + scale: Scale<f64>, + commit: Option<CommitCounter>, + ) -> DamageSet<i32, Physical> { + self.0.damage_since(scale, commit) + } + + fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> { + self.0.opaque_regions(scale) + } + + fn alpha(&self) -> f32 { + self.0.alpha() + } + + fn kind(&self) -> Kind { + self.0.kind() + } +} + +impl RenderElement<GlesRenderer> for ResizeRenderElement { + fn draw( + &self, + frame: &mut GlesFrame<'_>, + src: Rectangle<f64, Buffer>, + dst: Rectangle<i32, Physical>, + damage: &[Rectangle<i32, Physical>], + ) -> Result<(), GlesError> { + RenderElement::<GlesRenderer>::draw(&self.0, frame, src, dst, damage)?; + Ok(()) + } + + fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> { + self.0.underlying_storage(renderer) + } +} + +impl<'render> RenderElement<TtyRenderer<'render>> for ResizeRenderElement { + fn draw( + &self, + frame: &mut TtyFrame<'_, '_>, + src: Rectangle<f64, Buffer>, + dst: Rectangle<i32, Physical>, + damage: &[Rectangle<i32, Physical>], + ) -> Result<(), TtyRendererError<'render>> { + let gles_frame = frame.as_gles_frame(); + RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage)?; + Ok(()) + } + + fn underlying_storage(&self, renderer: &mut TtyRenderer<'render>) -> Option<UnderlyingStorage> { + self.0.underlying_storage(renderer) + } +} diff --git a/src/render_helpers/shaders/crossfade.frag b/src/render_helpers/shaders/crossfade.frag deleted file mode 100644 index 56280ad2..00000000 --- a/src/render_helpers/shaders/crossfade.frag +++ /dev/null @@ -1,31 +0,0 @@ -#version 100 - -precision mediump float; - -uniform sampler2D tex_from; -uniform vec2 tex_from_loc; -uniform vec2 tex_from_size; - -uniform sampler2D tex_to; -uniform vec2 tex_to_loc; -uniform vec2 tex_to_size; - -uniform float alpha; -uniform float amount; - -uniform vec2 size; -varying vec2 v_coords; - -void main() { - vec2 coords_from = (v_coords - tex_from_loc) / tex_from_size; - vec2 coords_to = (v_coords - tex_to_loc) / tex_to_size; - - vec4 color_from = texture2D(tex_from, coords_from); - vec4 color_to = texture2D(tex_to, coords_to); - - vec4 color = mix(color_from, color_to, amount); - color = color * alpha; - - gl_FragColor = color; -} - diff --git a/src/render_helpers/shaders/mod.rs b/src/render_helpers/shaders/mod.rs index ebe0d08c..040cf503 100644 --- a/src/render_helpers/shaders/mod.rs +++ b/src/render_helpers/shaders/mod.rs @@ -1,3 +1,5 @@ +use std::cell::RefCell; + use smithay::backend::renderer::gles::{GlesPixelProgram, GlesRenderer, UniformName, UniformType}; use super::primary_gpu_pixel_shader_with_textures::PixelWithTexturesProgram; @@ -5,7 +7,8 @@ use super::renderer::NiriRenderer; pub struct Shaders { pub gradient_border: Option<GlesPixelProgram>, - pub crossfade: Option<PixelWithTexturesProgram>, + pub resize: Option<PixelWithTexturesProgram>, + pub custom_resize: RefCell<Option<PixelWithTexturesProgram>>, } impl Shaders { @@ -28,26 +31,29 @@ impl Shaders { }) .ok(); - let crossfade = PixelWithTexturesProgram::compile( + let resize = PixelWithTexturesProgram::compile( renderer, - include_str!("crossfade.frag"), + include_str!("resize.frag"), &[ - UniformName::new("tex_from_loc", UniformType::_2f), - UniformName::new("tex_from_size", UniformType::_2f), - UniformName::new("tex_to_loc", UniformType::_2f), - UniformName::new("tex_to_size", UniformType::_2f), - UniformName::new("amount", UniformType::_1f), + UniformName::new("input_to_curr_geo", UniformType::Matrix3x3), + UniformName::new("input_to_prev_geo", UniformType::Matrix3x3), + UniformName::new("input_to_next_geo", UniformType::Matrix3x3), + UniformName::new("geo_to_tex_prev", UniformType::Matrix3x3), + UniformName::new("geo_to_tex_next", UniformType::Matrix3x3), + UniformName::new("progress", UniformType::_1f), + UniformName::new("clamped_progress", UniformType::_1f), ], - &["tex_from", "tex_to"], + &["tex_prev", "tex_next"], ) .map_err(|err| { - warn!("error compiling crossfade shader: {err:?}"); + warn!("error compiling resize shader: {err:?}"); }) .ok(); Self { gradient_border, - crossfade, + resize, + custom_resize: RefCell::new(None), } } @@ -57,6 +63,20 @@ impl Shaders { data.get() .expect("shaders::init() must be called when creating the renderer") } + + pub fn replace_custom_resize_program( + &self, + program: Option<PixelWithTexturesProgram>, + ) -> Option<PixelWithTexturesProgram> { + self.custom_resize.replace(program) + } + + pub fn resize(&self) -> Option<PixelWithTexturesProgram> { + self.custom_resize + .borrow() + .clone() + .or_else(|| self.resize.clone()) + } } pub fn init(renderer: &mut GlesRenderer) { @@ -66,3 +86,36 @@ pub fn init(renderer: &mut GlesRenderer) { error!("shaders were already compiled"); } } + +pub fn set_custom_resize_program(renderer: &mut GlesRenderer, src: Option<&str>) { + let program = if let Some(src) = src { + match PixelWithTexturesProgram::compile( + renderer, + src, + &[ + UniformName::new("input_to_curr_geo", UniformType::Matrix3x3), + UniformName::new("input_to_prev_geo", UniformType::Matrix3x3), + UniformName::new("input_to_next_geo", UniformType::Matrix3x3), + UniformName::new("geo_to_tex_prev", UniformType::Matrix3x3), + UniformName::new("geo_to_tex_next", UniformType::Matrix3x3), + UniformName::new("progress", UniformType::_1f), + UniformName::new("clamped_progress", UniformType::_1f), + ], + &["tex_prev", "tex_next"], + ) { + Ok(program) => Some(program), + Err(err) => { + warn!("error compiling custom resize shader: {err:?}"); + return; + } + } + } else { + None + }; + + if let Some(prev) = Shaders::get(renderer).replace_custom_resize_program(program) { + if let Err(err) = prev.destroy(renderer) { + warn!("error destroying previous custom resize shader: {err:?}"); + } + } +} diff --git a/src/render_helpers/shaders/resize.frag b/src/render_helpers/shaders/resize.frag new file mode 100644 index 00000000..d77b9163 --- /dev/null +++ b/src/render_helpers/shaders/resize.frag @@ -0,0 +1,41 @@ +#version 100 + +precision mediump float; + +varying vec2 v_coords; +uniform vec2 size; + +uniform mat3 input_to_curr_geo; +uniform mat3 input_to_prev_geo; +uniform mat3 input_to_next_geo; + +uniform sampler2D tex_prev; +uniform mat3 geo_to_tex_prev; + +uniform sampler2D tex_next; +uniform mat3 geo_to_tex_next; + +uniform float progress; +uniform float clamped_progress; + +uniform float alpha; + +vec4 crossfade() { + vec3 coords_curr_geo = input_to_curr_geo * vec3(v_coords, 1.0); + + vec3 coords_tex_prev = geo_to_tex_prev * coords_curr_geo; + vec4 color_prev = texture2D(tex_prev, vec2(coords_tex_prev)); + + vec3 coords_tex_next = geo_to_tex_next * coords_curr_geo; + vec4 color_next = texture2D(tex_next, vec2(coords_tex_next)); + + vec4 color = mix(color_prev, color_next, clamped_progress); + return color; +} + |
