aboutsummaryrefslogtreecommitdiff
path: root/src/render_helpers
diff options
context:
space:
mode:
Diffstat (limited to 'src/render_helpers')
-rw-r--r--src/render_helpers/border.rs (renamed from src/render_helpers/gradient.rs)63
-rw-r--r--src/render_helpers/clipped_surface.rs269
-rw-r--r--src/render_helpers/mod.rs6
-rw-r--r--src/render_helpers/primary_gpu_pixel_shader.rs97
-rw-r--r--src/render_helpers/resize.rs21
-rw-r--r--src/render_helpers/shader_element.rs (renamed from src/render_helpers/primary_gpu_pixel_shader_with_textures.rs)118
-rw-r--r--src/render_helpers/shaders/border.frag87
-rw-r--r--src/render_helpers/shaders/clipped_surface.frag77
-rw-r--r--src/render_helpers/shaders/gradient_border.frag35
-rw-r--r--src/render_helpers/shaders/mod.rs67
-rw-r--r--src/render_helpers/shaders/resize-epilogue.frag9
-rw-r--r--src/render_helpers/shaders/resize-prelude.frag22
-rw-r--r--src/render_helpers/shaders/resize_epilogue.frag27
-rw-r--r--src/render_helpers/shaders/resize_prelude.frag51
14 files changed, 715 insertions, 234 deletions
diff --git a/src/render_helpers/gradient.rs b/src/render_helpers/border.rs
index 958c2f9e..bf72a370 100644
--- a/src/render_helpers/gradient.rs
+++ b/src/render_helpers/border.rs
@@ -1,30 +1,40 @@
-use glam::Vec2;
+use std::collections::HashMap;
+
+use glam::{Mat3, Vec2};
+use niri_config::CornerRadius;
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
-use smithay::backend::renderer::gles::element::PixelShaderElement;
use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, Uniform};
use smithay::backend::renderer::utils::{CommitCounter, DamageSet};
use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Transform};
-use super::primary_gpu_pixel_shader::PrimaryGpuPixelShaderRenderElement;
use super::renderer::NiriRenderer;
-use super::shaders::Shaders;
+use super::shader_element::{ShaderProgram, ShaderRenderElement};
+use super::shaders::{mat3_uniform, Shaders};
use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
-/// Renders a sub- or super-rect of an angled linear gradient like CSS linear-gradient(angle, a, b).
+/// Renders a wide variety of borders and border parts.
+///
+/// This includes:
+/// * sub- or super-rect of an angled linear gradient like CSS linear-gradient(angle, a, b).
+/// * corner rounding.
+/// * as a background rectangle and as parts of a border line.
#[derive(Debug)]
-pub struct GradientRenderElement(PrimaryGpuPixelShaderRenderElement);
+pub struct BorderRenderElement(ShaderRenderElement);
-impl GradientRenderElement {
+impl BorderRenderElement {
+ #[allow(clippy::too_many_arguments)]
pub fn new(
- renderer: &mut impl NiriRenderer,
+ shader: ShaderProgram,
scale: Scale<f64>,
area: Rectangle<i32, Logical>,
gradient_area: Rectangle<i32, Logical>,
color_from: [f32; 4],
color_to: [f32; 4],
angle: f32,
- ) -> Option<Self> {
- let shader = Shaders::get(renderer).gradient_border.clone()?;
+ geometry: Rectangle<i32, Logical>,
+ border_width: f32,
+ corner_radius: CornerRadius,
+ ) -> Self {
let grad_offset = (area.loc - gradient_area.loc).to_f64().to_physical(scale);
let grad_dir = Vec2::from_angle(angle);
@@ -42,9 +52,24 @@ impl GradientRenderElement {
grad_vec = -grad_vec;
}
- let elem = PixelShaderElement::new(
+ let area_physical = area.to_physical_precise_round(scale);
+ let area_loc = Vec2::new(area_physical.loc.x, area_physical.loc.y);
+ let area_size = Vec2::new(area_physical.size.w, area_physical.size.h);
+
+ let geo = geometry.to_physical_precise_round(scale);
+ let geo_loc = Vec2::new(geo.loc.x, geo.loc.y);
+ let geo_size = Vec2::new(geo.size.w, geo.size.h);
+
+ let input_to_geo =
+ Mat3::from_scale(area_size) * Mat3::from_translation((area_loc - geo_loc) / area_size);
+ let corner_radius = corner_radius.scaled_by(scale.x as f32);
+ let border_width = border_width * scale.x as f32;
+
+ let elem = ShaderRenderElement::new(
shader,
+ HashMap::new(),
area,
+ area.size.to_f64().to_buffer(scale, Transform::Normal),
None,
1.,
vec![
@@ -53,14 +78,22 @@ impl GradientRenderElement {
Uniform::new("grad_offset", (grad_offset.x as f32, grad_offset.y as f32)),
Uniform::new("grad_width", w),
Uniform::new("grad_vec", grad_vec.to_array()),
+ mat3_uniform("input_to_geo", input_to_geo),
+ Uniform::new("geo_size", geo_size.to_array()),
+ Uniform::new("outer_radius", <[f32; 4]>::from(corner_radius)),
+ Uniform::new("border_width", border_width),
],
Kind::Unspecified,
);
- Some(Self(PrimaryGpuPixelShaderRenderElement(elem)))
+ Self(elem)
+ }
+
+ pub fn shader(renderer: &mut impl NiriRenderer) -> Option<&ShaderProgram> {
+ Shaders::get(renderer).border.as_ref()
}
}
-impl Element for GradientRenderElement {
+impl Element for BorderRenderElement {
fn id(&self) -> &Id {
self.0.id()
}
@@ -102,7 +135,7 @@ impl Element for GradientRenderElement {
}
}
-impl RenderElement<GlesRenderer> for GradientRenderElement {
+impl RenderElement<GlesRenderer> for BorderRenderElement {
fn draw(
&self,
frame: &mut GlesFrame<'_>,
@@ -118,7 +151,7 @@ impl RenderElement<GlesRenderer> for GradientRenderElement {
}
}
-impl<'render> RenderElement<TtyRenderer<'render>> for GradientRenderElement {
+impl<'render> RenderElement<TtyRenderer<'render>> for BorderRenderElement {
fn draw(
&self,
frame: &mut TtyFrame<'_, '_>,
diff --git a/src/render_helpers/clipped_surface.rs b/src/render_helpers/clipped_surface.rs
new file mode 100644
index 00000000..69c2a012
--- /dev/null
+++ b/src/render_helpers/clipped_surface.rs
@@ -0,0 +1,269 @@
+use glam::{Mat3, Vec2};
+use niri_config::CornerRadius;
+use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
+use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
+use smithay::backend::renderer::gles::{
+ GlesError, GlesFrame, GlesRenderer, GlesTexProgram, Uniform,
+};
+use smithay::backend::renderer::utils::{CommitCounter, DamageSet};
+use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Transform};
+
+use super::renderer::{AsGlesFrame as _, NiriRenderer};
+use super::shaders::{mat3_uniform, Shaders};
+use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
+
+#[derive(Debug)]
+pub struct ClippedSurfaceRenderElement<R: NiriRenderer> {
+ inner: WaylandSurfaceRenderElement<R>,
+ program: GlesTexProgram,
+ corner_radius: CornerRadius,
+ geometry: Rectangle<i32, Logical>,
+ input_to_geo: Mat3,
+}
+
+impl<R: NiriRenderer> ClippedSurfaceRenderElement<R> {
+ pub fn new(
+ elem: WaylandSurfaceRenderElement<R>,
+ scale: Scale<f64>,
+ geometry: Rectangle<i32, Logical>,
+ program: GlesTexProgram,
+ corner_radius: CornerRadius,
+ ) -> Self {
+ let elem_geo = elem.geometry(scale);
+
+ let elem_geo_loc = Vec2::new(elem_geo.loc.x as f32, elem_geo.loc.y as f32);
+ let elem_geo_size = Vec2::new(elem_geo.size.w as f32, elem_geo.size.h as f32);
+
+ let geo = geometry.to_physical_precise_round(scale);
+ let geo_loc = Vec2::new(geo.loc.x, geo.loc.y);
+ let geo_size = Vec2::new(geo.size.w, geo.size.h);
+
+ let buf_size = elem.buffer_size().unwrap();
+ let buf_size = Vec2::new(buf_size.w as f32, buf_size.h as f32);
+
+ let view = elem.view().unwrap();
+ let src_loc = Vec2::new(view.src.loc.x as f32, view.src.loc.y as f32);
+ let src_size = Vec2::new(view.src.size.w as f32, view.src.size.h as f32);
+
+ let transform = elem.transform();
+ // HACK: ??? for some reason flipped ones are fine.
+ let transform = match transform {
+ Transform::_90 => Transform::_270,
+ Transform::_270 => Transform::_90,
+ x => x,
+ };
+ let transform_matrix = Mat3::from_translation(Vec2::new(0.5, 0.5))
+ * Mat3::from_cols_array(transform.matrix().as_ref())
+ * Mat3::from_translation(-Vec2::new(0.5, 0.5));
+
+ // FIXME: y_inverted
+ let input_to_geo = transform_matrix * Mat3::from_scale(elem_geo_size / geo_size)
+ * Mat3::from_translation((elem_geo_loc - geo_loc) / elem_geo_size)
+ // Apply viewporter src.
+ * Mat3::from_scale(buf_size / src_size)
+ * Mat3::from_translation(-src_loc / buf_size);
+
+ Self {
+ inner: elem,
+ program,
+ corner_radius,
+ geometry,
+ input_to_geo,
+ }
+ }
+
+ pub fn shader(renderer: &mut R) -> Option<&GlesTexProgram> {
+ Shaders::get(renderer).clipped_surface.as_ref()
+ }
+
+ pub fn will_clip(
+ elem: &WaylandSurfaceRenderElement<R>,
+ scale: Scale<f64>,
+ geometry: Rectangle<i32, Logical>,
+ corner_radius: CornerRadius,
+ ) -> bool {
+ let elem_geo = elem.geometry(scale);
+ let geo = geometry.to_physical_precise_round(scale);
+
+ if corner_radius == CornerRadius::default() {
+ !geo.contains_rect(elem_geo)
+ } else {
+ let corners = Self::rounded_corners(geometry.to_f64(), corner_radius);
+ let corners = corners
+ .into_iter()
+ .map(|rect| rect.to_physical_precise_round(scale));
+ let geo = Rectangle::subtract_rects_many([geo], corners);
+ !Rectangle::subtract_rects_many([elem_geo], geo).is_empty()
+ }
+ }
+
+ fn rounded_corners(
+ geo: Rectangle<f64, Logical>,
+ corner_radius: CornerRadius,
+ ) -> [Rectangle<f64, Logical>; 4] {
+ let top_left = corner_radius.top_left as f64;
+ let top_right = corner_radius.top_right as f64;
+ let bottom_right = corner_radius.bottom_right as f64;
+ let bottom_left = corner_radius.bottom_left as f64;
+
+ [
+ Rectangle::from_loc_and_size(geo.loc, (top_left, top_left)),
+ Rectangle::from_loc_and_size(
+ (geo.loc.x + geo.size.w - top_right, geo.loc.y),
+ (top_right, top_right),
+ ),
+ Rectangle::from_loc_and_size(
+ (
+ geo.loc.x + geo.size.w - bottom_right,
+ geo.loc.y + geo.size.h - bottom_right,
+ ),
+ (bottom_right, bottom_right),
+ ),
+ Rectangle::from_loc_and_size(
+ (geo.loc.x, geo.loc.y + geo.size.h - bottom_left),
+ (bottom_left, bottom_left),
+ ),
+ ]
+ }
+}
+
+impl<R: NiriRenderer> Element for ClippedSurfaceRenderElement<R> {
+ 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 src(&self) -> Rectangle<f64, Buffer> {
+ self.inner.src()
+ }
+
+ fn transform(&self) -> Transform {
+ self.inner.transform()
+ }
+
+ fn damage_since(
+ &self,
+ scale: Scale<f64>,
+ commit: Option<CommitCounter>,
+ ) -> DamageSet<i32, Physical> {
+ // FIXME: radius changes need to cause damage.
+ let damage = self.inner.damage_since(scale, commit);
+
+ // Intersect with geometry, since we're clipping by it.
+ let mut geo = self.geometry.to_physical_precise_round(scale);
+ geo.loc -= self.geometry(scale).loc;
+ damage
+ .into_iter()
+ .filter_map(|rect| rect.intersection(geo))
+ .collect()
+ }
+
+ fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> {
+ let regions = self.inner.opaque_regions(scale);
+
+ // Intersect with geometry, since we're clipping by it.
+ let mut geo = self.geometry.to_physical_precise_round(scale);
+ geo.loc -= self.geometry(scale).loc;
+ let regions = regions
+ .into_iter()
+ .filter_map(|rect| rect.intersection(geo));
+
+ // Subtract the rounded corners.
+ if self.corner_radius == CornerRadius::default() {
+ regions.collect()
+ } else {
+ let corners = Self::rounded_corners(self.geometry.to_f64(), self.corner_radius);
+
+ let elem_loc = self.geometry(scale).loc;
+ let corners = corners.into_iter().map(|rect| {
+ let mut rect = rect.to_physical_precise_round(scale);
+ rect.loc -= elem_loc;
+ rect
+ });
+
+ Rectangle::subtract_rects_many(regions, corners)
+ }
+ }
+
+ fn alpha(&self) -> f32 {
+ self.inner.alpha()
+ }
+
+ fn kind(&self) -> Kind {
+ self.inner.kind()
+ }
+}
+
+impl RenderElement<GlesRenderer> for ClippedSurfaceRenderElement<GlesRenderer> {
+ fn draw(
+ &self,
+ frame: &mut GlesFrame<'_>,
+ src: Rectangle<f64, Buffer>,
+ dst: Rectangle<i32, Physical>,
+ damage: &[Rectangle<i32, Physical>],
+ ) -> Result<(), GlesError> {
+ frame.override_default_tex_program(
+ self.program.clone(),
+ vec![
+ Uniform::new(
+ "geo_size",
+ (self.geometry.size.w as f32, self.geometry.size.h as f32),
+ ),
+ Uniform::new("corner_radius", <[f32; 4]>::from(self.corner_radius)),
+ mat3_uniform("input_to_geo", self.input_to_geo),
+ ],
+ );
+ RenderElement::<GlesRenderer>::draw(&self.inner, frame, src, dst, damage)?;
+ 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 ClippedSurfaceRenderElement<TtyRenderer<'render>>
+{
+ fn draw(
+ &self,
+ frame: &mut TtyFrame<'render, '_>,
+ src: Rectangle<f64, Buffer>,
+ dst: Rectangle<i32, Physical>,
+ damage: &[Rectangle<i32, Physical>],
+ ) -> Result<(), TtyRendererError<'render>> {
+ frame.as_gles_frame().override_default_tex_program(
+ self.program.clone(),
+ vec![
+ Uniform::new(
+ "geo_size",
+ (self.geometry.size.w as f32, self.geometry.size.h as f32),
+ ),
+ Uniform::new("corner_radius", <[f32; 4]>::from(self.corner_radius)),
+ mat3_uniform("input_to_geo", self.input_to_geo),
+ ],
+ );
+ RenderElement::draw(&self.inner, frame, src, dst, damage)?;
+ frame.as_gles_frame().clear_tex_program_override();
+ 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
+ }
+}
diff --git a/src/render_helpers/mod.rs b/src/render_helpers/mod.rs
index 16abe1ae..ebc4b49e 100644
--- a/src/render_helpers/mod.rs
+++ b/src/render_helpers/mod.rs
@@ -16,15 +16,15 @@ use smithay::wayland::shm;
use self::primary_gpu_texture::PrimaryGpuTextureRenderElement;
-pub mod gradient;
+pub mod border;
+pub mod clipped_surface;
pub mod offscreen;
-pub mod primary_gpu_pixel_shader;
-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 shader_element;
pub mod shaders;
pub mod snapshot;
pub mod surface;
diff --git a/src/render_helpers/primary_gpu_pixel_shader.rs b/src/render_helpers/primary_gpu_pixel_shader.rs
deleted file mode 100644
index 79abc283..00000000
--- a/src/render_helpers/primary_gpu_pixel_shader.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
-use smithay::backend::renderer::gles::element::PixelShaderElement;
-use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer};
-use smithay::backend::renderer::utils::{CommitCounter, DamageSet};
-use smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform};
-
-use super::renderer::AsGlesFrame;
-use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
-
-/// Wrapper for a pixel shader from the primary GPU for rendering with the primary GPU.
-#[derive(Debug)]
-pub struct PrimaryGpuPixelShaderRenderElement(pub PixelShaderElement);
-
-impl Element for PrimaryGpuPixelShaderRenderElement {
- 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 PrimaryGpuPixelShaderRenderElement {
- fn draw(
- &self,
- frame: &mut GlesFrame<'_>,
- src: Rectangle<f64, Buffer>,
- dst: Rectangle<i32, Physical>,
- damage: &[Rectangle<i32, Physical>],
- ) -> Result<(), GlesError> {
- let gles_frame = frame.as_gles_frame();
- RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage)?;
- 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 PrimaryGpuPixelShaderRenderElement {
- 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> {
- // If scanout for things other than Wayland buffers is implemented, this will need to take
- // the target GPU into account.
- None
- }
-}
diff --git a/src/render_helpers/resize.rs b/src/render_helpers/resize.rs
index 9641c8d8..8ba897ef 100644
--- a/src/render_helpers/resize.rs
+++ b/src/render_helpers/resize.rs
@@ -1,25 +1,24 @@
use std::collections::HashMap;
use glam::{Mat3, Vec2};
+use niri_config::CornerRadius;
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::{
- PixelWithTexturesProgram, PrimaryGpuPixelShaderWithTexturesRenderElement,
-};
use super::renderer::{AsGlesFrame, NiriRenderer};
+use super::shader_element::{ShaderProgram, ShaderRenderElement};
use super::shaders::{mat3_uniform, Shaders};
use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
#[derive(Debug)]
-pub struct ResizeRenderElement(PrimaryGpuPixelShaderWithTexturesRenderElement);
+pub struct ResizeRenderElement(ShaderRenderElement);
impl ResizeRenderElement {
#[allow(clippy::too_many_arguments)]
pub fn new(
- shader: PixelWithTexturesProgram,
+ shader: ShaderProgram,
area: Rectangle<i32, Logical>,
scale: Scale<f64>,
texture_prev: (GlesTexture, Rectangle<i32, Physical>),
@@ -28,6 +27,8 @@ impl ResizeRenderElement {
size_next: Size<i32, Logical>,
progress: f32,
clamped_progress: f32,
+ corner_radius: CornerRadius,
+ clip_to_geometry: bool,
result_alpha: f32,
) -> Self {
let curr_geo = area;
@@ -84,9 +85,13 @@ impl ResizeRenderElement {
* Mat3::from_scale(size_next / tex_next_geo_size * scale);
let curr_geo_size = curr_geo_size * scale;
+ let corner_radius = corner_radius
+ .scaled_by(scale.x)
+ .fit_to(curr_geo_size.x, curr_geo_size.y);
+ let clip_to_geometry = if clip_to_geometry { 1. } else { 0. };
// Create the shader.
- Self(PrimaryGpuPixelShaderWithTexturesRenderElement::new(
+ Self(ShaderRenderElement::new(
shader,
HashMap::from([
(String::from("niri_tex_prev"), texture_prev),
@@ -105,12 +110,14 @@ impl ResizeRenderElement {
mat3_uniform("niri_geo_to_tex_next", geo_to_tex_next),
Uniform::new("niri_progress", progress),
Uniform::new("niri_clamped_progress", clamped_progress),
+ Uniform::new("niri_corner_radius", <[f32; 4]>::from(corner_radius)),
+ Uniform::new("niri_clip_to_geometry", clip_to_geometry),
],
Kind::Unspecified,
))
}
- pub fn shader(renderer: &mut impl NiriRenderer) -> Option<PixelWithTexturesProgram> {
+ pub fn shader(renderer: &mut impl NiriRenderer) -> Option<ShaderProgram> {
Shaders::get(renderer).resize()
}
}
diff --git a/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs b/src/render_helpers/shader_element.rs
index e088a965..c17764ee 100644
--- a/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs
+++ b/src/render_helpers/shader_element.rs
@@ -9,18 +9,17 @@ use smithay::backend::renderer::gles::{
UniformDesc, UniformName,
};
use smithay::backend::renderer::utils::CommitCounter;
+use smithay::backend::renderer::DebugFlags;
use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Size};
use super::renderer::AsGlesFrame;
use super::resources::Resources;
use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
-/// Wrapper for a pixel shader from the primary GPU for rendering with the primary GPU.
-///
-/// The shader accepts textures as input.
+/// Renders a shader with optional texture input, on the primary GPU.
#[derive(Debug)]
-pub struct PrimaryGpuPixelShaderWithTexturesRenderElement {
- shader: PixelWithTexturesProgram,
+pub struct ShaderRenderElement {
+ shader: ShaderProgram,
textures: HashMap<String, GlesTexture>,
id: Id,
commit_counter: CommitCounter,
@@ -33,10 +32,17 @@ pub struct PrimaryGpuPixelShaderWithTexturesRenderElement {
}
#[derive(Debug, Clone)]
-pub struct PixelWithTexturesProgram(Rc<PixelWithTexturesProgramInner>);
+pub struct ShaderProgram(Rc<ShaderProgramInner>);
#[derive(Debug)]
-struct PixelWithTexturesProgramInner {
+struct ShaderProgramInner {
+ normal: ShaderProgramInternal,
+ debug: ShaderProgramInternal,
+ uniform_tint: ffi::types::GLint,
+}
+
+#[derive(Debug)]
+struct ShaderProgramInternal {
program: ffi::types::GLuint,
uniform_tex_matrix: ffi::types::GLint,
uniform_matrix: ffi::types::GLint,
@@ -54,10 +60,12 @@ unsafe fn compile_program(
additional_uniforms: &[UniformName<'_>],
texture_uniforms: &[&str],
// destruction_callback_sender: Sender<CleanupResource>,
-) -> Result<PixelWithTexturesProgram, GlesError> {
- let shader = src;
-
- let program = unsafe { link_program(gl, include_str!("shaders/texture.vert"), shader)? };
+) -> Result<ShaderProgram, GlesError> {
+ let shader = format!("#version 100\n{}", src);
+ let program = unsafe { link_program(gl, include_str!("shaders/texture.vert"), &shader)? };
+ let debug_shader = format!("#version 100\n#define DEBUG_FLAGS\n{}", src);
+ let debug_program =
+ unsafe { link_program(gl, include_str!("shaders/texture.vert"), &debug_shader)? };
let vert = CStr::from_bytes_with_nul(b"vert\0").expect("NULL terminated");
let vert_position = CStr::from_bytes_with_nul(b"vert_position\0").expect("NULL terminated");
@@ -65,9 +73,10 @@ unsafe fn compile_program(
let tex_matrix = CStr::from_bytes_with_nul(b"tex_matrix\0").expect("NULL terminated");
let size = CStr::from_bytes_with_nul(b"niri_size\0").expect("NULL terminated");
let alpha = CStr::from_bytes_with_nul(b"niri_alpha\0").expect("NULL terminated");
+ let tint = CStr::from_bytes_with_nul(b"niri_tint\0").expect("NULL terminated");
- Ok(PixelWithTexturesProgram(Rc::new(
- PixelWithTexturesProgramInner {
+ Ok(ShaderProgram(Rc::new(ShaderProgramInner {
+ normal: ShaderProgramInternal {
program,
uniform_matrix: gl
.GetUniformLocation(program, matrix.as_ptr() as *const ffi::types::GLchar),
@@ -106,10 +115,60 @@ unsafe fn compile_program(
})
.collect(),
},
- )))
+ debug: ShaderProgramInternal {
+ program: debug_program,
+ uniform_matrix: gl
+ .GetUniformLocation(debug_program, matrix.as_ptr() as *const ffi::types::GLchar),
+ uniform_tex_matrix: gl.GetUniformLocation(
+ debug_program,
+ tex_matrix.as_ptr() as *const ffi::types::GLchar,
+ ),
+ uniform_size: gl
+ .GetUniformLocation(debug_program, size.as_ptr() as *const ffi::types::GLchar),
+ uniform_alpha: gl
+ .GetUniformLocation(debug_program, alpha.as_ptr() as *const ffi::types::GLchar),
+ attrib_vert: gl
+ .GetAttribLocation(debug_program, vert.as_ptr() as *const ffi::types::GLchar),
+ attrib_vert_position: gl.GetAttribLocation(
+ debug_program,
+ vert_position.as_ptr() as *const ffi::types::GLchar,
+ ),
+ additional_uniforms: additional_uniforms
+ .iter()
+ .map(|uniform| {
+ let name =
+ CString::new(uniform.name.as_bytes()).expect("Interior null in name");
+ let location = gl.GetUniformLocation(
+ debug_program,
+ name.as_ptr() as *const ffi::types::GLchar,
+ );
+ (
+ uniform.name.clone().into_owned(),
+ UniformDesc {
+ location,
+ type_: uniform.type_,
+ },
+ )
+ })
+ .collect(),
+ texture_uniforms: texture_uniforms
+ .iter()
+ .map(|name_| {
+ let name = CString::new(name_.as_bytes()).expect("Interior null in name");
+ let location = gl.GetUniformLocation(
+ debug_program,
+ name.as_ptr() as *const ffi::types::GLchar,
+ );
+ (name_.to_string(), location)
+ })
+ .collect(),
+ },
+ uniform_tint: gl
+ .GetUniformLocation(debug_program, tint.as_ptr() as *const ffi::types::GLchar),
+ })))
}
-impl PixelWithTexturesProgram {
+impl ShaderProgram {
pub fn compile(
renderer: &mut GlesRenderer,
src: &str,
@@ -123,15 +182,16 @@ impl PixelWithTexturesProgram {
pub fn destroy(self, renderer: &mut GlesRenderer) -> Result<(), GlesError> {
renderer.with_context(move |gl| unsafe {
- gl.DeleteProgram(self.0.program);
+ gl.DeleteProgram(self.0.normal.program);
+ gl.DeleteProgram(self.0.debug.program);
})
}
}
-impl PrimaryGpuPixelShaderWithTexturesRenderElement {
+impl ShaderRenderElement {
#[allow(clippy::too_many_arguments)]
pub fn new(
- shader: PixelWithTexturesProgram,
+ shader: ShaderProgram,
textures: HashMap<String, GlesTexture>,
area: Rectangle<i32, Logical>,
size: Size<f64, Buffer>,
@@ -158,7 +218,7 @@ impl PrimaryGpuPixelShaderWithTexturesRenderElement {
}
}
-impl Element for PrimaryGpuPixelShaderWithTexturesRenderElement {
+impl Element for ShaderRenderElement {
fn id(&self) -> &Id {
&self.id
}
@@ -191,7 +251,7 @@ impl Element for PrimaryGpuPixelShaderWithTexturesRenderElement {
}
}
-impl RenderElement<GlesRenderer> for PrimaryGpuPixelShaderWithTexturesRenderElement {
+impl RenderElement<GlesRenderer> for ShaderRenderElement {
fn draw(
&self,
frame: &mut GlesFrame<'_>,
@@ -274,7 +334,14 @@ impl RenderElement<GlesRenderer> for PrimaryGpuPixelShaderWithTexturesRenderElem
//apply output transformation
matrix = Mat3::from_cols_array(frame.projection()) * matrix;
- let program = &self.shader.0;
+ let has_debug = !frame.debug_flags().is_empty();
+ let has_tint = frame.debug_flags().contains(DebugFlags::TINT);
+
+ let program = if has_debug {
+ &self.shader.0.debug
+ } else {
+ &self.shader.0.normal
+ };
// render
frame.with_context(move |gl| -> Result<(), GlesError> {
@@ -317,6 +384,11 @@ impl RenderElement<GlesRenderer> for PrimaryGpuPixelShaderWithTexturesRenderElem
gl.Uniform2f(program.uniform_size, dest.size.w as f32, dest.size.h as f32);
gl.Uniform1f(program.uniform_alpha, self.alpha);
+ let tint = if has_tint { 1.0f32 } else { 0.0f32 };
+ if has_debug {
+ gl.Uniform1f(self.shader.0.uniform_tint, tint);
+ }
+
for uniform in &self.additional_uniforms {
let desc =
program
@@ -408,9 +480,7 @@ impl RenderElement<GlesRenderer> for PrimaryGpuPixelShaderWithTexturesRenderElem
}
}
-impl<'render> RenderElement<TtyRenderer<'render>>
- for PrimaryGpuPixelShaderWithTexturesRenderElement
-{
+impl<'render> RenderElement<TtyRenderer<'render>> for ShaderRenderElement {
fn draw(
&self,
frame: &mut TtyFrame<'_, '_>,
diff --git a/src/render_helpers/shaders/border.frag b/src/render_helpers/shaders/border.frag
new file mode 100644
index 00000000..89edb363
--- /dev/null
+++ b/src/render_helpers/shaders/border.frag
@@ -0,0 +1,87 @@
+precision mediump float;
+
+#if defined(DEBUG_FLAGS)
+uniform float niri_tint;
+#endif
+
+uniform float niri_alpha;
+
+uniform vec2 niri_size;
+varying vec2 niri_v_coords;
+
+uniform vec4 color_from;
+uniform vec4 color_to;
+uniform vec2 grad_offset;
+uniform float grad_width;
+uniform vec2 grad_vec;
+
+uniform mat3 input_to_geo;
+uniform vec2 geo_size;
+uniform vec4 outer_radius;
+uniform float border_width;
+
+vec4 gradient_color(vec2 coords) {
+ coords = coords + grad_offset;
+
+ if ((grad_vec.x < 0.0 && 0.0 <= grad_vec.y) || (0.0 <= grad_vec.x && grad_vec.y < 0.0))
+ coords.x -= grad_width;
+
+ float frac = dot(coords, grad_vec) / dot(grad_vec, grad_vec);
+
+ if (grad_vec.y < 0.0)
+ frac += 1.0;
+
+ frac = clamp(frac, 0.0, 1.0);
+ return mix(color_from, color_to, frac);
+}
+
+float rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius) {
+ vec2 center;
+ float radius;
+
+ if (coords.x < corner_radius.x && coords.y < corner_radius.x) {
+ radius = corner_radius.x;
+ center = vec2(radius, radius);
+ } else if (size.x - corner_radius.y < coords.x && coords.y < corner_radius.y) {
+ radius = corner_radius.y;
+ center = vec2(size.x - radius, radius);
+ } else if (size.x - corner_radius.z < coords.x && size.y - corner_radius.z < coords.y) {
+ radius = corner_radius.z;
+ center = vec2(size.x - radius, size.y - radius);
+ } else if (coords.x < corner_radius.w && size.y - corner_radius.w < coords.y) {
+ radius = corner_radius.w;
+ center = vec2(radius, size.y - radius);
+ } else {
+ return 1.0;
+ }
+
+ float dist = distance(coords, center);
+ return 1.0 - smoothstep(radius - 0.5, radius + 0.5, dist);
+}
+
+void main() {
+ vec4 color = gradient_color(niri_v_coords * niri_size);
+
+ vec3 coords_geo = input_to_geo * vec3(niri_v_coords, 1.0);
+ color = color * rounding_alpha(coords_geo.xy, geo_size, outer_radius);
+
+ if (border_width > 0.0) {