aboutsummaryrefslogtreecommitdiff
path: root/src/render_helpers/resize.rs
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-04-21 20:10:35 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2024-04-21 20:16:54 +0400
commit49f5402669012be33f8cd111311c3e39fd4751c0 (patch)
treea90eab10775b63cddb0772923d43875ee13ef8b6 /src/render_helpers/resize.rs
parent2ecbb3f6f8ceacdccc66a597e372c70029325dbf (diff)
downloadniri-49f5402669012be33f8cd111311c3e39fd4751c0.tar.gz
niri-49f5402669012be33f8cd111311c3e39fd4751c0.tar.bz2
niri-49f5402669012be33f8cd111311c3e39fd4751c0.zip
Implement window-resize custom-shader
Diffstat (limited to 'src/render_helpers/resize.rs')
-rw-r--r--src/render_helpers/resize.rs199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/render_helpers/resize.rs b/src/render_helpers/resize.rs
new file mode 100644
index 00000000..2242c99e
--- /dev/null
+++ b/src/render_helpers/resize.rs
@@ -0,0 +1,199 @@
+use std::collections::HashMap;
+
+use glam::{Mat3, Vec2};
+use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
+use smithay::backend::renderer::gles::{
+ GlesError, GlesFrame, GlesRenderer, GlesTexture, Uniform, UniformValue,
+};
+use smithay::backend::renderer::utils::{CommitCounter, DamageSet};
+use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Size, Transform};
+
+use super::primary_gpu_pixel_shader_with_textures::PrimaryGpuPixelShaderWithTexturesRenderElement;
+use super::renderer::AsGlesFrame;
+use super::shaders::Shaders;
+use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
+
+#[derive(Debug)]
+pub struct ResizeRenderElement(PrimaryGpuPixelShaderWithTexturesRenderElement);
+
+impl ResizeRenderElement {
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ renderer: &mut GlesRenderer,
+ area: Rectangle<i32, Logical>,
+ scale: Scale<f64>,
+ texture_prev: (GlesTexture, Rectangle<i32, Physical>),
+ size_prev: Size<i32, Logical>,
+ texture_next: (GlesTexture, Rectangle<i32, Physical>),
+ size_next: Size<i32, Logical>,
+ progress: f32,
+ clamped_progress: f32,
+ result_alpha: f32,
+ ) -> Option<Self> {
+ let curr_geo = area;
+
+ let (texture_prev, tex_prev_geo) = texture_prev;
+ let (texture_next, tex_next_geo) = texture_next;
+
+ let scale_prev = area.size.to_f64() / size_prev.to_f64();
+ let scale_next = area.size.to_f64() / size_next.to_f64();
+
+ // Compute the area necessary to fit a crossfade.
+ let tex_prev_geo_scaled = tex_prev_geo.to_f64().upscale(scale_prev);
+ let tex_next_geo_scaled = tex_next_geo.to_f64().upscale(scale_next);
+ let combined_geo = tex_prev_geo_scaled.merge(tex_next_geo_scaled);
+
+ let size = combined_geo
+ .size
+ .to_logical(1.)
+ .to_buffer(1., Transform::Normal);
+
+ let area = Rectangle::from_loc_and_size(
+ area.loc + combined_geo.loc.to_logical(scale).to_i32_round(),
+ combined_geo.size.to_logical(scale).to_i32_round(),
+ );
+
+ // Convert Smithay types into glam types.
+ let area_loc = Vec2::new(area.loc.x as f32, area.loc.y as f32);
+ let area_size = Vec2::new(area.size.w as f32, area.size.h as f32);
+
+ let curr_geo_loc = Vec2::new(curr_geo.loc.x as f32, curr_geo.loc.y as f32);
+ let curr_geo_size = Vec2::new(curr_geo.size.w as f32, curr_geo.size.h as f32);
+
+ let tex_prev_geo_loc = Vec2::new(tex_prev_geo.loc.x as f32, tex_prev_geo.loc.y as f32);
+ let tex_prev_geo_size = Vec2::new(tex_prev_geo.size.w as f32, tex_prev_geo.size.h as f32);
+
+ let tex_next_geo_loc = Vec2::new(tex_next_geo.loc.x as f32, tex_next_geo.loc.y as f32);
+ let tex_next_geo_size = Vec2::new(tex_next_geo.size.w as f32, tex_next_geo.size.h as f32);
+
+ let size_prev = Vec2::new(size_prev.w as f32, size_prev.h as f32);
+ let size_next = Vec2::new(size_next.w as f32, size_next.h as f32);
+
+ let scale = Vec2::new(scale.x as f32, scale.y as f32);
+
+ // Compute the transformation matrices.
+ let input_to_curr_geo = Mat3::from_scale(area_size / curr_geo_size)
+ * Mat3::from_translation((area_loc - curr_geo_loc) / area_size);
+ let input_to_prev_geo = Mat3::from_scale(area_size / size_prev)
+ * Mat3::from_translation((area_loc - curr_geo_loc) / area_size);
+ let input_to_next_geo = Mat3::from_scale(area_size / size_next)
+ * Mat3::from_translation((area_loc - curr_geo_loc) / area_size);
+
+ let geo_to_tex_prev = Mat3::from_translation(-tex_prev_geo_loc / tex_prev_geo_size)
+ * Mat3::from_scale(size_prev / tex_prev_geo_size * scale);
+ let geo_to_tex_next = Mat3::from_translation(-tex_next_geo_loc / tex_next_geo_size)
+ * Mat3::from_scale(size_next / tex_next_geo_size * scale);
+
+ // Create the shader.
+ let make_uniform = |name, mat: Mat3| {
+ Uniform::new(
+ name,
+ UniformValue::Matrix3x3 {
+ matrices: vec![mat.to_cols_array()],
+ transpose: false,
+ },
+ )
+ };
+
+ Shaders::get(renderer).resize().map(|shader| {
+ Self(PrimaryGpuPixelShaderWithTexturesRenderElement::new(
+ shader,
+ HashMap::from([
+ (String::from("tex_prev"), texture_prev),
+ (String::from("tex_next"), texture_next),
+ ]),
+ area,
+ size,
+ None,
+ result_alpha,
+ vec![
+ make_uniform("input_to_curr_geo", input_to_curr_geo),
+ make_uniform("input_to_prev_geo", input_to_prev_geo),
+ make_uniform("input_to_next_geo", input_to_next_geo),
+ make_uniform("geo_to_tex_prev", geo_to_tex_prev),
+ make_uniform("geo_to_tex_next", geo_to_tex_next),
+ Uniform::new("progress", progress),
+ Uniform::new("clamped_progress", clamped_progress),
+ ],
+ Kind::Unspecified,
+ ))
+ })
+ }
+}
+
+impl Element for ResizeRenderElement {
+ fn id(&self) -> &Id {
+ self.0.id()
+ }
+
+ fn current_commit(&self) -> CommitCounter {
+ self.0.current_commit()
+ }
+
+ fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
+ self.0.geometry(scale)
+ }
+
+ fn transform(&self) -> Transform {
+ self.0.transform()
+ }
+
+ fn src(&self) -> Rectangle<f64, Buffer> {
+ self.0.src()
+ }
+
+ fn damage_since(
+ &self,
+ scale: Scale<f64>,
+ commit: Option<CommitCounter>,
+ ) -> DamageSet<i32, Physical> {
+ self.0.damage_since(scale, commit)
+ }
+
+ fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> {
+ self.0.opaque_regions(scale)
+ }
+
+ fn alpha(&self) -> f32 {
+ self.0.alpha()
+ }
+
+ fn kind(&self) -> Kind {
+ self.0.kind()
+ }
+}
+
+impl RenderElement<GlesRenderer> for ResizeRenderElement {
+ fn draw(
+ &self,
+ frame: &mut GlesFrame<'_>,
+ src: Rectangle<f64, Buffer>,
+ dst: Rectangle<i32, Physical>,
+ damage: &[Rectangle<i32, Physical>],
+ ) -> Result<(), GlesError> {
+ RenderElement::<GlesRenderer>::draw(&self.0, frame, src, dst, damage)?;
+ Ok(())
+ }
+
+ fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> {
+ self.0.underlying_storage(renderer)
+ }
+}
+
+impl<'render> RenderElement<TtyRenderer<'render>> for ResizeRenderElement {
+ fn draw(
+ &self,
+ frame: &mut TtyFrame<'_, '_>,
+ src: Rectangle<f64, Buffer>,
+ dst: Rectangle<i32, Physical>,
+ damage: &[Rectangle<i32, Physical>],
+ ) -> Result<(), TtyRendererError<'render>> {
+ let gles_frame = frame.as_gles_frame();
+ RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage)?;
+ Ok(())
+ }
+
+ fn underlying_storage(&self, renderer: &mut TtyRenderer<'render>) -> Option<UnderlyingStorage> {
+ self.0.underlying_storage(renderer)
+ }
+}