mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
Implement window-resize custom-shader
This commit is contained in:
+50
-13
@@ -477,7 +477,7 @@ pub struct HotkeyOverlay {
|
|||||||
pub skip_at_startup: bool,
|
pub skip_at_startup: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
||||||
pub struct Animations {
|
pub struct Animations {
|
||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
pub off: bool,
|
pub off: bool,
|
||||||
@@ -593,19 +593,25 @@ impl Default for WindowMovementAnim {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct WindowResizeAnim(pub Animation);
|
pub struct WindowResizeAnim {
|
||||||
|
pub anim: Animation,
|
||||||
|
pub custom_shader: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for WindowResizeAnim {
|
impl Default for WindowResizeAnim {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self(Animation {
|
Self {
|
||||||
|
anim: Animation {
|
||||||
off: false,
|
off: false,
|
||||||
kind: AnimationKind::Spring(SpringParams {
|
kind: AnimationKind::Spring(SpringParams {
|
||||||
damping_ratio: 1.,
|
damping_ratio: 1.,
|
||||||
stiffness: 800,
|
stiffness: 800,
|
||||||
epsilon: 0.0001,
|
epsilon: 0.0001,
|
||||||
}),
|
}),
|
||||||
})
|
},
|
||||||
|
custom_shader: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,7 +1197,9 @@ where
|
|||||||
ctx: &mut knuffel::decode::Context<S>,
|
ctx: &mut knuffel::decode::Context<S>,
|
||||||
) -> Result<Self, DecodeError<S>> {
|
) -> Result<Self, DecodeError<S>> {
|
||||||
let default = Self::default().0;
|
let default = Self::default().0;
|
||||||
Ok(Self(Animation::decode_node(node, ctx, default)?))
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
||||||
|
Ok(false)
|
||||||
|
})?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1204,7 +1212,9 @@ where
|
|||||||
ctx: &mut knuffel::decode::Context<S>,
|
ctx: &mut knuffel::decode::Context<S>,
|
||||||
) -> Result<Self, DecodeError<S>> {
|
) -> Result<Self, DecodeError<S>> {
|
||||||
let default = Self::default().0;
|
let default = Self::default().0;
|
||||||
Ok(Self(Animation::decode_node(node, ctx, default)?))
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
||||||
|
Ok(false)
|
||||||
|
})?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1217,7 +1227,9 @@ where
|
|||||||
ctx: &mut knuffel::decode::Context<S>,
|
ctx: &mut knuffel::decode::Context<S>,
|
||||||
) -> Result<Self, DecodeError<S>> {
|
) -> Result<Self, DecodeError<S>> {
|
||||||
let default = Self::default().0;
|
let default = Self::default().0;
|
||||||
Ok(Self(Animation::decode_node(node, ctx, default)?))
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
||||||
|
Ok(false)
|
||||||
|
})?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1230,7 +1242,9 @@ where
|
|||||||
ctx: &mut knuffel::decode::Context<S>,
|
ctx: &mut knuffel::decode::Context<S>,
|
||||||
) -> Result<Self, DecodeError<S>> {
|
) -> Result<Self, DecodeError<S>> {
|
||||||
let default = Self::default().0;
|
let default = Self::default().0;
|
||||||
Ok(Self(Animation::decode_node(node, ctx, default)?))
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
||||||
|
Ok(false)
|
||||||
|
})?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1243,7 +1257,9 @@ where
|
|||||||
ctx: &mut knuffel::decode::Context<S>,
|
ctx: &mut knuffel::decode::Context<S>,
|
||||||
) -> Result<Self, DecodeError<S>> {
|
) -> Result<Self, DecodeError<S>> {
|
||||||
let default = Self::default().0;
|
let default = Self::default().0;
|
||||||
Ok(Self(Animation::decode_node(node, ctx, default)?))
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
||||||
|
Ok(false)
|
||||||
|
})?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1255,8 +1271,21 @@ where
|
|||||||
node: &knuffel::ast::SpannedNode<S>,
|
node: &knuffel::ast::SpannedNode<S>,
|
||||||
ctx: &mut knuffel::decode::Context<S>,
|
ctx: &mut knuffel::decode::Context<S>,
|
||||||
) -> Result<Self, DecodeError<S>> {
|
) -> Result<Self, DecodeError<S>> {
|
||||||
let default = Self::default().0;
|
let default = Self::default().anim;
|
||||||
Ok(Self(Animation::decode_node(node, ctx, default)?))
|
let mut custom_shader = None;
|
||||||
|
let anim = Animation::decode_node(node, ctx, default, |child, ctx| {
|
||||||
|
if &**child.node_name == "custom-shader" {
|
||||||
|
custom_shader = parse_arg_node("custom-shader", child, ctx)?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
anim,
|
||||||
|
custom_shader,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1269,7 +1298,9 @@ where
|
|||||||
ctx: &mut knuffel::decode::Context<S>,
|
ctx: &mut knuffel::decode::Context<S>,
|
||||||
) -> Result<Self, DecodeError<S>> {
|
) -> Result<Self, DecodeError<S>> {
|
||||||
let default = Self::default().0;
|
let default = Self::default().0;
|
||||||
Ok(Self(Animation::decode_node(node, ctx, default)?))
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
||||||
|
Ok(false)
|
||||||
|
})?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1278,6 +1309,10 @@ impl Animation {
|
|||||||
node: &knuffel::ast::SpannedNode<S>,
|
node: &knuffel::ast::SpannedNode<S>,
|
||||||
ctx: &mut knuffel::decode::Context<S>,
|
ctx: &mut knuffel::decode::Context<S>,
|
||||||
default: Self,
|
default: Self,
|
||||||
|
mut process_children: impl FnMut(
|
||||||
|
&knuffel::ast::SpannedNode<S>,
|
||||||
|
&mut knuffel::decode::Context<S>,
|
||||||
|
) -> Result<bool, DecodeError<S>>,
|
||||||
) -> Result<Self, DecodeError<S>> {
|
) -> Result<Self, DecodeError<S>> {
|
||||||
#[derive(Default, PartialEq)]
|
#[derive(Default, PartialEq)]
|
||||||
struct OptionalEasingParams {
|
struct OptionalEasingParams {
|
||||||
@@ -1360,6 +1395,7 @@ impl Animation {
|
|||||||
easing_params.curve = Some(parse_arg_node("curve", child, ctx)?);
|
easing_params.curve = Some(parse_arg_node("curve", child, ctx)?);
|
||||||
}
|
}
|
||||||
name_str => {
|
name_str => {
|
||||||
|
if !process_children(child, ctx)? {
|
||||||
ctx.emit_error(DecodeError::unexpected(
|
ctx.emit_error(DecodeError::unexpected(
|
||||||
child,
|
child,
|
||||||
"node",
|
"node",
|
||||||
@@ -1368,6 +1404,7 @@ impl Animation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let kind = if let Some(spring_params) = spring_params {
|
let kind = if let Some(spring_params) = spring_params {
|
||||||
// Configured spring.
|
// Configured spring.
|
||||||
|
|||||||
+9
-2
@@ -491,8 +491,15 @@ impl Tty {
|
|||||||
warn!("error binding wl-display in EGL: {err:?}");
|
warn!("error binding wl-display in EGL: {err:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
resources::init(renderer.as_gles_renderer());
|
let gles_renderer = renderer.as_gles_renderer();
|
||||||
shaders::init(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.
|
// Create the dmabuf global.
|
||||||
let primary_formats = renderer.dmabuf_formats().collect::<HashSet<_>>();
|
let primary_formats = renderer.dmabuf_formats().collect::<HashSet<_>>();
|
||||||
|
|||||||
@@ -135,6 +135,12 @@ impl Winit {
|
|||||||
resources::init(renderer);
|
resources::init(renderer);
|
||||||
shaders::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);
|
niri.add_output(self.output.clone(), None, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -238,7 +238,7 @@ impl Options {
|
|||||||
center_focused_column: layout.center_focused_column,
|
center_focused_column: layout.center_focused_column,
|
||||||
preset_widths,
|
preset_widths,
|
||||||
default_width,
|
default_width,
|
||||||
animations: config.animations,
|
animations: config.animations.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-15
@@ -16,10 +16,10 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::animation::Animation;
|
use crate::animation::Animation;
|
||||||
use crate::niri_render_elements;
|
use crate::niri_render_elements;
|
||||||
use crate::render_helpers::crossfade::CrossfadeRenderElement;
|
|
||||||
use crate::render_helpers::offscreen::OffscreenRenderElement;
|
use crate::render_helpers::offscreen::OffscreenRenderElement;
|
||||||
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||||
use crate::render_helpers::renderer::NiriRenderer;
|
use crate::render_helpers::renderer::NiriRenderer;
|
||||||
|
use crate::render_helpers::resize::ResizeRenderElement;
|
||||||
use crate::render_helpers::shaders::Shaders;
|
use crate::render_helpers::shaders::Shaders;
|
||||||
use crate::render_helpers::snapshot::RenderSnapshot;
|
use crate::render_helpers::snapshot::RenderSnapshot;
|
||||||
use crate::render_helpers::{render_to_encompassing_texture, RenderTarget, ToRenderElement};
|
use crate::render_helpers::{render_to_encompassing_texture, RenderTarget, ToRenderElement};
|
||||||
@@ -73,7 +73,7 @@ niri_render_elements! {
|
|||||||
FocusRing = FocusRingRenderElement,
|
FocusRing = FocusRingRenderElement,
|
||||||
SolidColor = SolidColorRenderElement,
|
SolidColor = SolidColorRenderElement,
|
||||||
Offscreen = RescaleRenderElement<OffscreenRenderElement>,
|
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 = self.window.size().to_point() - size_from.to_point();
|
||||||
let change = max(change.x.abs(), change.y.abs());
|
let change = max(change.x.abs(), change.y.abs());
|
||||||
if change > RESIZE_ANIMATION_THRESHOLD {
|
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 {
|
self.resize_animation = Some(ResizeAnimation {
|
||||||
anim,
|
anim,
|
||||||
size_from,
|
size_from,
|
||||||
@@ -527,12 +527,12 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
|
|
||||||
let gles_renderer = renderer.as_gles_renderer();
|
let gles_renderer = renderer.as_gles_renderer();
|
||||||
|
|
||||||
// If we're resizing, try to render a crossfade, or a fallback.
|
// If we're resizing, try to render a shader, or a fallback.
|
||||||
let mut crossfade = None;
|
let mut resize_shader = None;
|
||||||
let mut crossfade_fallback = None;
|
let mut resize_fallback = None;
|
||||||
|
|
||||||
if let Some(resize) = &self.resize_animation {
|
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) {
|
if let Some(texture_from) = resize.snapshot.texture(gles_renderer, scale, target) {
|
||||||
let window_elements =
|
let window_elements =
|
||||||
self.window
|
self.window
|
||||||
@@ -548,7 +548,7 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
if let Some((texture_current, _sync_point, texture_current_geo)) = current {
|
if let Some((texture_current, _sync_point, texture_current_geo)) = current {
|
||||||
let elem = CrossfadeRenderElement::new(
|
let elem = ResizeRenderElement::new(
|
||||||
gles_renderer,
|
gles_renderer,
|
||||||
area,
|
area,
|
||||||
scale,
|
scale,
|
||||||
@@ -556,20 +556,21 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
resize.snapshot.size,
|
resize.snapshot.size,
|
||||||
(texture_current, texture_current_geo),
|
(texture_current, texture_current_geo),
|
||||||
window_size,
|
window_size,
|
||||||
|
resize.anim.value() as f32,
|
||||||
resize.anim.clamped_value().clamp(0., 1.) as f32,
|
resize.anim.clamped_value().clamp(0., 1.) as f32,
|
||||||
alpha,
|
alpha,
|
||||||
)
|
)
|
||||||
.expect("we checked the crossfade shader above");
|
.expect("we checked the resize shader above");
|
||||||
self.window
|
self.window
|
||||||
.set_offscreen_element_id(Some(elem.id().clone()));
|
.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.]);
|
let fallback_buffer = SolidColorBuffer::new(area.size, [1., 0., 0., 1.]);
|
||||||
crossfade_fallback = Some(
|
resize_fallback = Some(
|
||||||
SolidColorRenderElement::from_buffer(
|
SolidColorRenderElement::from_buffer(
|
||||||
&fallback_buffer,
|
&fallback_buffer,
|
||||||
area.loc.to_physical_precise_round(scale),
|
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.
|
// If we're not resizing, render the window itself.
|
||||||
let mut window = None;
|
let mut window = None;
|
||||||
if crossfade.is_none() && crossfade_fallback.is_none() {
|
if resize_shader.is_none() && resize_fallback.is_none() {
|
||||||
window = Some(
|
window = Some(
|
||||||
self.window
|
self.window
|
||||||
.render(renderer, window_render_loc, scale, alpha, target)
|
.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()
|
.into_iter()
|
||||||
.chain(crossfade_fallback)
|
.chain(resize_fallback)
|
||||||
.chain(window.into_iter().flatten());
|
.chain(window.into_iter().flatten());
|
||||||
|
|
||||||
let elem = self.effective_border_width().map(|width| {
|
let elem = self.effective_border_width().map(|width| {
|
||||||
|
|||||||
@@ -1118,14 +1118,14 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
for col in &mut self.columns[col_idx + 1..] {
|
for col in &mut self.columns[col_idx + 1..] {
|
||||||
col.animate_move_from_with_config(
|
col.animate_move_from_with_config(
|
||||||
offset,
|
offset,
|
||||||
self.options.animations.window_resize.0,
|
self.options.animations.window_resize.anim,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for col in &mut self.columns[..=col_idx] {
|
for col in &mut self.columns[..=col_idx] {
|
||||||
col.animate_move_from_with_config(
|
col.animate_move_from_with_config(
|
||||||
-offset,
|
-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
|
// Synchronize the horizontal view movement with the resize so that it looks nice. This
|
||||||
// is especially important for always-centered view.
|
// is especially important for always-centered view.
|
||||||
let config = if started_resize_anim {
|
let config = if started_resize_anim {
|
||||||
self.options.animations.window_resize.0
|
self.options.animations.window_resize.anim
|
||||||
} else {
|
} else {
|
||||||
self.options.animations.horizontal_view_movement.0
|
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..] {
|
for tile in &mut self.tiles[tile_idx + 1..] {
|
||||||
tile.animate_move_y_from_with_config(
|
tile.animate_move_y_from_with_config(
|
||||||
offset,
|
offset,
|
||||||
self.options.animations.window_resize.0,
|
self.options.animations.window_resize.anim,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -29,6 +29,8 @@ use smithay::reexports::calloop::EventLoop;
|
|||||||
use smithay::reexports::wayland_server::Display;
|
use smithay::reexports::wayland_server::Display;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
|
const DEFAULT_LOG_FILTER: &str = "niri=debug,smithay::backend::renderer::gles=error";
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// Set backtrace defaults if not set.
|
// Set backtrace defaults if not set.
|
||||||
if env::var_os("RUST_BACKTRACE").is_none() {
|
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);
|
let env_filter = EnvFilter::builder().parse_lossy(directives);
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.compact()
|
.compact()
|
||||||
|
|||||||
+12
-1
@@ -107,7 +107,9 @@ use crate::protocols::gamma_control::GammaControlManagerState;
|
|||||||
use crate::protocols::screencopy::{Screencopy, ScreencopyManagerState};
|
use crate::protocols::screencopy::{Screencopy, ScreencopyManagerState};
|
||||||
use crate::pw_utils::{Cast, PipeWire};
|
use crate::pw_utils::{Cast, PipeWire};
|
||||||
use crate::render_helpers::renderer::NiriRenderer;
|
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::scroll_tracker::ScrollTracker;
|
||||||
use crate::ui::config_error_notification::ConfigErrorNotification;
|
use crate::ui::config_error_notification::ConfigErrorNotification;
|
||||||
use crate::ui::exit_confirm_dialog::ExitConfirmDialog;
|
use crate::ui::exit_confirm_dialog::ExitConfirmDialog;
|
||||||
@@ -895,6 +897,15 @@ impl State {
|
|||||||
window_rules_changed = true;
|
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;
|
*old_config = config;
|
||||||
|
|
||||||
// Release the borrow.
|
// Release the borrow.
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,6 @@ use smithay::wayland::shm;
|
|||||||
|
|
||||||
use self::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
use self::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||||
|
|
||||||
pub mod crossfade;
|
|
||||||
pub mod gradient;
|
pub mod gradient;
|
||||||
pub mod offscreen;
|
pub mod offscreen;
|
||||||
pub mod primary_gpu_pixel_shader;
|
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 primary_gpu_texture;
|
||||||
pub mod render_elements;
|
pub mod render_elements;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
|
pub mod resize;
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
pub mod shaders;
|
pub mod shaders;
|
||||||
pub mod snapshot;
|
pub mod snapshot;
|
||||||
|
|||||||
@@ -120,6 +120,12 @@ impl PixelWithTexturesProgram {
|
|||||||
compile_program(gl, src, additional_uniforms, texture_uniforms)
|
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 {
|
impl PrimaryGpuPixelShaderWithTexturesRenderElement {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use smithay::backend::renderer::gles::{GlesPixelProgram, GlesRenderer, UniformName, UniformType};
|
use smithay::backend::renderer::gles::{GlesPixelProgram, GlesRenderer, UniformName, UniformType};
|
||||||
|
|
||||||
use super::primary_gpu_pixel_shader_with_textures::PixelWithTexturesProgram;
|
use super::primary_gpu_pixel_shader_with_textures::PixelWithTexturesProgram;
|
||||||
@@ -5,7 +7,8 @@ use super::renderer::NiriRenderer;
|
|||||||
|
|
||||||
pub struct Shaders {
|
pub struct Shaders {
|
||||||
pub gradient_border: Option<GlesPixelProgram>,
|
pub gradient_border: Option<GlesPixelProgram>,
|
||||||
pub crossfade: Option<PixelWithTexturesProgram>,
|
pub resize: Option<PixelWithTexturesProgram>,
|
||||||
|
pub custom_resize: RefCell<Option<PixelWithTexturesProgram>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Shaders {
|
impl Shaders {
|
||||||
@@ -28,26 +31,29 @@ impl Shaders {
|
|||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
let crossfade = PixelWithTexturesProgram::compile(
|
let resize = PixelWithTexturesProgram::compile(
|
||||||
renderer,
|
renderer,
|
||||||
include_str!("crossfade.frag"),
|
include_str!("resize.frag"),
|
||||||
&[
|
&[
|
||||||
UniformName::new("tex_from_loc", UniformType::_2f),
|
UniformName::new("input_to_curr_geo", UniformType::Matrix3x3),
|
||||||
UniformName::new("tex_from_size", UniformType::_2f),
|
UniformName::new("input_to_prev_geo", UniformType::Matrix3x3),
|
||||||
UniformName::new("tex_to_loc", UniformType::_2f),
|
UniformName::new("input_to_next_geo", UniformType::Matrix3x3),
|
||||||
UniformName::new("tex_to_size", UniformType::_2f),
|
UniformName::new("geo_to_tex_prev", UniformType::Matrix3x3),
|
||||||
UniformName::new("amount", UniformType::_1f),
|
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| {
|
.map_err(|err| {
|
||||||
warn!("error compiling crossfade shader: {err:?}");
|
warn!("error compiling resize shader: {err:?}");
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
gradient_border,
|
gradient_border,
|
||||||
crossfade,
|
resize,
|
||||||
|
custom_resize: RefCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +63,20 @@ impl Shaders {
|
|||||||
data.get()
|
data.get()
|
||||||
.expect("shaders::init() must be called when creating the renderer")
|
.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) {
|
pub fn init(renderer: &mut GlesRenderer) {
|
||||||
@@ -66,3 +86,36 @@ pub fn init(renderer: &mut GlesRenderer) {
|
|||||||
error!("shaders were already compiled");
|
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:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 color = crossfade();
|
||||||
|
|
||||||
|
gl_FragColor = color * alpha;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -224,6 +224,47 @@ animations {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### `custom-shader`
|
||||||
|
|
||||||
|
<sup>Since: 0.1.6, experimental</sup>
|
||||||
|
|
||||||
|
You can write a custom shader for drawing the window during a resize animation.
|
||||||
|
|
||||||
|
See [this example shader](./examples/resize-custom-shader.frag) for a full documentation with several animations to experiment with.
|
||||||
|
|
||||||
|
If a custom shader fails to compile, niri will print a warning and fall back to the default, or previous successfully compiled shader.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> Custom shaders do not have a backwards compatibility guarantee.
|
||||||
|
> I may need to change their interface as I'm developing new features.
|
||||||
|
|
||||||
|
```
|
||||||
|
animations {
|
||||||
|
window-resize {
|
||||||
|
spring damping-ratio=1.0 stiffness=800 epsilon=0.0001
|
||||||
|
|
||||||
|
custom-shader r"
|
||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_coords;
|
||||||
|
uniform mat3 input_to_curr_geo;
|
||||||
|
uniform sampler2D tex_next;
|
||||||
|
uniform mat3 geo_to_tex_next;
|
||||||
|
uniform float alpha;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 coords_curr_geo = input_to_curr_geo * vec3(v_coords, 1.0);
|
||||||
|
vec3 coords_tex_next = geo_to_tex_next * coords_curr_geo;
|
||||||
|
vec4 color = texture2D(tex_next, vec2(coords_tex_next));
|
||||||
|
gl_FragColor = color * alpha;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### `config-notification-open-close`
|
#### `config-notification-open-close`
|
||||||
|
|
||||||
The open/close animation of the config parse error and new default config notifications.
|
The open/close animation of the config parse error and new default config notifications.
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
#version 100
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
// Coordinates of the current pixel.
|
||||||
|
//
|
||||||
|
// These range from 0 to 1 over the whole area of the shader. The location and
|
||||||
|
// the size of the area are unspecified, but niri will make it large enough to
|
||||||
|
// accomodate a crossfade.
|
||||||
|
//
|
||||||
|
// You very likely want to convert these coordinates to geometry coordinates
|
||||||
|
// before using them (see below).
|
||||||
|
varying vec2 v_coords;
|
||||||
|
|
||||||
|
// Pixel size of the whole area of the shader.
|
||||||
|
uniform vec2 size;
|
||||||
|
|
||||||
|
// Matrix that converts the input v_coords into coordinates inside the current
|
||||||
|
// window geometry.
|
||||||
|
//
|
||||||
|
// The window geometry is its "visible bounds" from the user's perspective.
|
||||||
|
// After applying this matrix, the 0 to 1 coordinate range will correspond to
|
||||||
|
// the current geometry (in the middle of a resize), and pixels outside the
|
||||||
|
// geometry will have coordinates below 0 or above 1.
|
||||||
|
uniform mat3 input_to_curr_geo;
|
||||||
|
|
||||||
|
// Matrix that converts the input v_coords into coordinates inside the previous
|
||||||
|
// (before resize) window geometry.
|
||||||
|
uniform mat3 input_to_prev_geo;
|
||||||
|
|
||||||
|
// Matrix that converts the input v_coords into coordinates inside the next
|
||||||
|
// (after resize) window geometry.
|
||||||
|
uniform mat3 input_to_next_geo;
|
||||||
|
|
||||||
|
// Previous (before resize) window texture.
|
||||||
|
uniform sampler2D tex_prev;
|
||||||
|
|
||||||
|
// Matrix that converts geometry coordinates into the previous window texture
|
||||||
|
// coordinates.
|
||||||
|
//
|
||||||
|
// The window texture can and will go outside the geometry (for client-side
|
||||||
|
// decoration shadows for example), which is why this matrix is necessary.
|
||||||
|
uniform mat3 geo_to_tex_prev;
|
||||||
|
|
||||||
|
// Next (after resize) window texture.
|
||||||
|
uniform sampler2D tex_next;
|
||||||
|
|
||||||
|
// Matrix that converts geometry coordinates into the next window texture
|
||||||
|
// coordinates.
|
||||||
|
uniform mat3 geo_to_tex_next;
|
||||||
|
|
||||||
|
// Unclamped progress of the resize.
|
||||||
|
//
|
||||||
|
// Goes from 0 to 1 but may overshoot and oscillate.
|
||||||
|
uniform float progress;
|
||||||
|
|
||||||
|
// Clamped progress of the resize.
|
||||||
|
//
|
||||||
|
// Goes from 0 to 1, but will stop at 1 as soon as it first reaches 1. Will not
|
||||||
|
// overshoot or oscillate.
|
||||||
|
uniform float clamped_progress;
|
||||||
|
|
||||||
|
// Additional opacity to apply to the final color.
|
||||||
|
uniform float alpha;
|
||||||
|
|
||||||
|
// Example: fill the current geometry with a solid vertical gradient.
|
||||||
|
vec4 solid_gradient() {
|
||||||
|
vec3 coords = input_to_curr_geo * vec3(v_coords, 1.0);
|
||||||
|
|
||||||
|
vec4 color = vec4(0.0);
|
||||||
|
|
||||||
|
// Paint only the area inside the current geometry.
|
||||||
|
if (0.0 <= coords.x && coords.x <= 1.0
|
||||||
|
&& 0.0 <= coords.y && coords.y <= 1.0)
|
||||||
|
{
|
||||||
|
vec4 from = vec4(1.0, 0.0, 0.0, 1.0);
|
||||||
|
vec4 to = vec4(0.0, 1.0, 0.0, 1.0);
|
||||||
|
color = mix(from, to, coords.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: crossfade between previous and next texture, stretched to the
|
||||||
|
// current geometry.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: next texture, stretched to the current geometry.
|
||||||
|
vec4 stretch_next() {
|
||||||
|
vec3 coords_curr_geo = input_to_curr_geo * vec3(v_coords, 1.0);
|
||||||
|
vec3 coords_tex_next = geo_to_tex_next * coords_curr_geo;
|
||||||
|
vec4 color = texture2D(tex_next, vec2(coords_tex_next));
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: next texture, stretched to the current geometry if smaller, and
|
||||||
|
// cropped if bigger.
|
||||||
|
vec4 stretch_or_crop_next() {
|
||||||
|
vec3 coords_curr_geo = input_to_curr_geo * vec3(v_coords, 1.0);
|
||||||
|
vec3 coords_next_geo = input_to_next_geo * vec3(v_coords, 1.0);
|
||||||
|
|
||||||
|
vec3 coords_stretch = geo_to_tex_next * coords_curr_geo;
|
||||||
|
vec3 coords_crop = geo_to_tex_next * coords_next_geo;
|
||||||
|
|
||||||
|
// If the crop coord is smaller than the stretch coord, then the next
|
||||||
|
// texture size is bigger than the current geometry, which means that we
|
||||||
|
// can crop.
|
||||||
|
vec3 coords = coords_stretch;
|
||||||
|
if (coords_crop.x < coords_stretch.x)
|
||||||
|
coords.x = coords_crop.x;
|
||||||
|
if (coords_crop.y < coords_stretch.y)
|
||||||
|
coords.y = coords_crop.y;
|
||||||
|
|
||||||
|
vec4 color = texture2D(tex_next, vec2(coords));
|
||||||
|
|
||||||
|
// However, when we crop, we also want to crop out anything outside the
|
||||||
|
// current geometry. This is because the area of the shader is unspecified
|
||||||
|
// and usually bigger than the current geometry, so if we don't fill pixels
|
||||||
|
// outside with transparency, the texture will leak out.
|
||||||
|
//
|
||||||
|
// When stretching, this is not an issue because the area outside will
|
||||||
|
// correspond to client-side decoration shadows, which are already supposed
|
||||||
|
// to be outside.
|
||||||
|
if (coords_crop.x < coords_stretch.x
|
||||||
|
&& (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x))
|
||||||
|
color = vec4(0.0);
|
||||||
|
if (coords_crop.y < coords_stretch.y
|
||||||
|
&& (coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y))
|
||||||
|
color = vec4(0.0);
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main entry point of the shader.
|
||||||
|
void main() {
|
||||||
|
// You can pick one of the example functions or write your own.
|
||||||
|
vec4 color = stretch_or_crop_next();
|
||||||
|
|
||||||
|
gl_FragColor = color * alpha;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user