render_helpers: Add GradientFadeTexture

This commit is contained in:
Ivan Molodetskikh
2025-11-16 11:26:40 +03:00
parent 661fcd42ad
commit b774fc1baf
5 changed files with 201 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
use smithay::backend::renderer::gles::{
GlesError, GlesFrame, GlesRenderer, GlesTexProgram, GlesTexture, Uniform,
};
use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};
use smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform};
use super::texture::TextureRenderElement;
use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
use crate::render_helpers::renderer::AsGlesFrame as _;
use crate::render_helpers::shaders::Shaders;
#[derive(Debug, Clone)]
pub struct GradientFadeTextureRenderElement {
inner: TextureRenderElement<GlesTexture>,
program: GradientFadeShader,
uniforms: Vec<Uniform<'static>>,
}
#[derive(Debug, Clone)]
pub struct GradientFadeShader(GlesTexProgram);
impl GradientFadeTextureRenderElement {
pub fn new(texture: TextureRenderElement<GlesTexture>, program: GradientFadeShader) -> Self {
let logical_w = texture.buffer().logical_size().w;
let logical_src_w = texture.logical_src().size.w;
let cutoff = if logical_src_w < logical_w {
// Texture is clipped, add a fade.
let cutoff = 1. - f64::min(18. / logical_src_w, 1.);
let full = logical_src_w / logical_w;
((cutoff * full) as f32, full as f32)
} else {
// Texture is displayed full-size, no cutoff necessary.
(1., 1.)
};
let uniforms = vec![Uniform::new("cutoff", cutoff)];
Self {
inner: texture,
program,
uniforms,
}
}
pub fn shader(renderer: &mut GlesRenderer) -> Option<GradientFadeShader> {
let program = Shaders::get(renderer).gradient_fade.clone();
program.map(GradientFadeShader)
}
}
impl Element for GradientFadeTextureRenderElement {
fn id(&self) -> &Id {
self.inner.id()
}
fn current_commit(&self) -> CommitCounter {
self.inner.current_commit()
}
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
self.inner.geometry(scale)
}
fn transform(&self) -> Transform {
self.inner.transform()
}
fn src(&self) -> Rectangle<f64, Buffer> {
self.inner.src()
}
fn damage_since(
&self,
scale: Scale<f64>,
commit: Option<CommitCounter>,
) -> DamageSet<i32, Physical> {
self.inner.damage_since(scale, commit)
}
fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {
self.inner.opaque_regions(scale)
}
fn alpha(&self) -> f32 {
self.inner.alpha()
}
fn kind(&self) -> Kind {
self.inner.kind()
}
}
impl RenderElement<GlesRenderer> for GradientFadeTextureRenderElement {
fn draw(
&self,
frame: &mut GlesFrame<'_, '_>,
src: Rectangle<f64, Buffer>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
) -> Result<(), GlesError> {
frame.override_default_tex_program(self.program.0.clone(), self.uniforms.clone());
RenderElement::<GlesRenderer>::draw(&self.inner, frame, src, dst, damage, opaque_regions)?;
frame.clear_tex_program_override();
Ok(())
}
fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> {
// If scanout for things other than Wayland buffers is implemented, this will need to take
// the target GPU into account.
None
}
}
impl<'render> RenderElement<TtyRenderer<'render>> for GradientFadeTextureRenderElement {
fn draw(
&self,
frame: &mut TtyFrame<'render, '_, '_>,
src: Rectangle<f64, Buffer>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
) -> Result<(), TtyRendererError<'render>> {
let gles_frame = frame.as_gles_frame();
RenderElement::<GlesRenderer>::draw(&self, gles_frame, src, dst, damage, opaque_regions)?;
Ok(())
}
fn underlying_storage(
&self,
_renderer: &mut TtyRenderer<'render>,
) -> Option<UnderlyingStorage<'_>> {
// If scanout for things other than Wayland buffers is implemented, this will need to take
// the target GPU into account.
None
}
}
+1
View File
@@ -22,6 +22,7 @@ pub mod border;
pub mod clipped_surface;
pub mod damage;
pub mod debug;
pub mod gradient_fade_texture;
pub mod memory;
pub mod offscreen;
pub mod primary_gpu_texture;
@@ -0,0 +1,47 @@
#version 100
//_DEFINES_
#if defined(EXTERNAL)
#extension GL_OES_EGL_image_external : require
#endif
precision highp float;
#if defined(EXTERNAL)
uniform samplerExternalOES tex;
#else
uniform sampler2D tex;
#endif
uniform float alpha;
varying vec2 v_coords;
#if defined(DEBUG_FLAGS)
uniform float tint;
#endif
// x is left edge, y is right edge of the gradient.
uniform vec2 cutoff;
void main() {
// Sample the texture.
vec4 color = texture2D(tex, v_coords);
#if defined(NO_ALPHA)
color = vec4(color.rgb, 1.0);
#endif
if (cutoff.x < cutoff.y) {
float fade = clamp((cutoff.y - v_coords.x) / (cutoff.y - cutoff.x), 0.0, 1.0);
color = color * fade;
}
// Apply final alpha and tint.
color = color * alpha;
#if defined(DEBUG_FLAGS)
if (tint == 1.0)
color = vec4(0.0, 0.2, 0.0, 0.2) + color * 0.8;
#endif
gl_FragColor = color;
}
+12
View File
@@ -14,6 +14,7 @@ pub struct Shaders {
pub shadow: Option<ShaderProgram>,
pub clipped_surface: Option<GlesTexProgram>,
pub resize: Option<ShaderProgram>,
pub gradient_fade: Option<GlesTexProgram>,
pub custom_resize: RefCell<Option<ShaderProgram>>,
pub custom_close: RefCell<Option<ShaderProgram>>,
pub custom_open: RefCell<Option<ShaderProgram>>,
@@ -96,11 +97,22 @@ impl Shaders {
})
.ok();
let gradient_fade = renderer
.compile_custom_texture_shader(
include_str!("gradient_fade.frag"),
&[UniformName::new("cutoff", UniformType::_2f)],
)
.map_err(|err| {
warn!("error compiling gradient fade shader: {err:?}");
})
.ok();
Self {
border,
shadow,
clipped_surface,
resize,
gradient_fade,
custom_resize: RefCell::new(None),
custom_close: RefCell::new(None),
custom_open: RefCell::new(None),
+5
View File
@@ -152,6 +152,11 @@ impl<T: Texture> TextureRenderElement<T> {
.or_else(|| self.src.map(|src| src.size))
.unwrap_or_else(|| self.buffer.logical_size())
}
pub fn logical_src(&self) -> Rectangle<f64, Logical> {
self.src
.unwrap_or_else(|| Rectangle::from_size(self.logical_size()))
}
}
impl<T: Texture> Element for TextureRenderElement<T> {