mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +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,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
||||
pub struct Animations {
|
||||
#[knuffel(child)]
|
||||
pub off: bool,
|
||||
@@ -593,19 +593,25 @@ impl Default for WindowMovementAnim {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct WindowResizeAnim(pub Animation);
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct WindowResizeAnim {
|
||||
pub anim: Animation,
|
||||
pub custom_shader: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for WindowResizeAnim {
|
||||
fn default() -> Self {
|
||||
Self(Animation {
|
||||
Self {
|
||||
anim: Animation {
|
||||
off: false,
|
||||
kind: AnimationKind::Spring(SpringParams {
|
||||
damping_ratio: 1.,
|
||||
stiffness: 800,
|
||||
epsilon: 0.0001,
|
||||
}),
|
||||
})
|
||||
},
|
||||
custom_shader: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1191,7 +1197,9 @@ where
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
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>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
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>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
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>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
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>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
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>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
let default = Self::default().0;
|
||||
Ok(Self(Animation::decode_node(node, ctx, default)?))
|
||||
let default = Self::default().anim;
|
||||
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>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
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>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
default: Self,
|
||||
mut process_children: impl FnMut(
|
||||
&knuffel::ast::SpannedNode<S>,
|
||||
&mut knuffel::decode::Context<S>,
|
||||
) -> Result<bool, DecodeError<S>>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
#[derive(Default, PartialEq)]
|
||||
struct OptionalEasingParams {
|
||||
@@ -1360,6 +1395,7 @@ impl Animation {
|
||||
easing_params.curve = Some(parse_arg_node("curve", child, ctx)?);
|
||||
}
|
||||
name_str => {
|
||||
if !process_children(child, ctx)? {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
child,
|
||||
"node",
|
||||
@@ -1368,6 +1404,7 @@ impl Animation {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let kind = if let Some(spring_params) = spring_params {
|
||||
// Configured spring.
|
||||
|
||||
+9
-2
@@ -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<_>>();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -238,7 +238,7 @@ impl Options {
|
||||
center_focused_column: layout.center_focused_column,
|
||||
preset_widths,
|
||||
default_width,
|
||||
animations: config.animations,
|
||||
animations: config.animations.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-15
@@ -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| {
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -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()
|
||||
|
||||
+12
-1
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 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:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`
|
||||
|
||||
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