diff options
| -rw-r--r-- | src/render_helpers/primary_gpu_pixel_shader_with_textures.rs | 4 | ||||
| -rw-r--r-- | src/render_helpers/resize.rs | 25 | ||||
| -rw-r--r-- | src/render_helpers/shaders/mod.rs | 66 | ||||
| -rw-r--r-- | src/render_helpers/shaders/resize-epilogue.frag | 8 | ||||
| -rw-r--r-- | src/render_helpers/shaders/resize-prelude.frag | 21 | ||||
| -rw-r--r-- | src/render_helpers/shaders/resize.frag | 43 | ||||
| -rw-r--r-- | src/render_helpers/shaders/texture.vert | 4 | ||||
| -rw-r--r-- | wiki/Configuration:-Animations.md | 22 | ||||
| -rw-r--r-- | wiki/examples/resize-custom-shader.frag | 129 |
9 files changed, 161 insertions, 161 deletions
diff --git a/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs b/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs index 45f93342..e088a965 100644 --- a/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs +++ b/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs @@ -63,8 +63,8 @@ unsafe fn compile_program( let vert_position = CStr::from_bytes_with_nul(b"vert_position\0").expect("NULL terminated"); let matrix = CStr::from_bytes_with_nul(b"matrix\0").expect("NULL terminated"); let tex_matrix = CStr::from_bytes_with_nul(b"tex_matrix\0").expect("NULL terminated"); - let size = CStr::from_bytes_with_nul(b"size\0").expect("NULL terminated"); - let alpha = CStr::from_bytes_with_nul(b"alpha\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"); Ok(PixelWithTexturesProgram(Rc::new( PixelWithTexturesProgramInner { diff --git a/src/render_helpers/resize.rs b/src/render_helpers/resize.rs index 2242c99e..73406c27 100644 --- a/src/render_helpers/resize.rs +++ b/src/render_helpers/resize.rs @@ -74,10 +74,9 @@ impl ResizeRenderElement { // 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 curr_geo_to_prev_geo = Mat3::from_scale(curr_geo_size / size_prev); + let curr_geo_to_next_geo = Mat3::from_scale(curr_geo_size / size_next); 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); @@ -99,21 +98,21 @@ impl ResizeRenderElement { Self(PrimaryGpuPixelShaderWithTexturesRenderElement::new( shader, HashMap::from([ - (String::from("tex_prev"), texture_prev), - (String::from("tex_next"), texture_next), + (String::from("niri_tex_prev"), texture_prev), + (String::from("niri_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), + make_uniform("niri_input_to_curr_geo", input_to_curr_geo), + make_uniform("niri_curr_geo_to_prev_geo", curr_geo_to_prev_geo), + make_uniform("niri_curr_geo_to_next_geo", curr_geo_to_next_geo), + make_uniform("niri_geo_to_tex_prev", geo_to_tex_prev), + make_uniform("niri_geo_to_tex_next", geo_to_tex_next), + Uniform::new("niri_progress", progress), + Uniform::new("niri_clamped_progress", clamped_progress), ], Kind::Unspecified, )) diff --git a/src/render_helpers/shaders/mod.rs b/src/render_helpers/shaders/mod.rs index 040cf503..f03e168a 100644 --- a/src/render_helpers/shaders/mod.rs +++ b/src/render_helpers/shaders/mod.rs @@ -1,6 +1,8 @@ use std::cell::RefCell; -use smithay::backend::renderer::gles::{GlesPixelProgram, GlesRenderer, UniformName, UniformType}; +use smithay::backend::renderer::gles::{ + GlesError, GlesPixelProgram, GlesRenderer, UniformName, UniformType, +}; use super::primary_gpu_pixel_shader_with_textures::PixelWithTexturesProgram; use super::renderer::NiriRenderer; @@ -31,24 +33,11 @@ impl Shaders { }) .ok(); - let resize = PixelWithTexturesProgram::compile( - renderer, - include_str!("resize.frag"), - &[ - 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"], - ) - .map_err(|err| { - warn!("error compiling resize shader: {err:?}"); - }) - .ok(); + let resize = compile_resize_program(renderer, include_str!("resize.frag")) + .map_err(|err| { + warn!("error compiling resize shader: {err:?}"); + }) + .ok(); Self { gradient_border, @@ -87,22 +76,33 @@ pub fn init(renderer: &mut GlesRenderer) { } } +fn compile_resize_program( + renderer: &mut GlesRenderer, + src: &str, +) -> Result<PixelWithTexturesProgram, GlesError> { + let mut program = include_str!("resize-prelude.frag").to_string(); + program.push_str(src); + program.push_str(include_str!("resize-epilogue.frag")); + + PixelWithTexturesProgram::compile( + renderer, + &program, + &[ + UniformName::new("niri_input_to_curr_geo", UniformType::Matrix3x3), + UniformName::new("niri_curr_geo_to_prev_geo", UniformType::Matrix3x3), + UniformName::new("niri_curr_geo_to_next_geo", UniformType::Matrix3x3), + UniformName::new("niri_geo_to_tex_prev", UniformType::Matrix3x3), + UniformName::new("niri_geo_to_tex_next", UniformType::Matrix3x3), + UniformName::new("niri_progress", UniformType::_1f), + UniformName::new("niri_clamped_progress", UniformType::_1f), + ], + &["niri_tex_prev", "niri_tex_next"], + ) +} + 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"], - ) { + match compile_resize_program(renderer, src) { Ok(program) => Some(program), Err(err) => { warn!("error compiling custom resize shader: {err:?}"); diff --git a/src/render_helpers/shaders/resize-epilogue.frag b/src/render_helpers/shaders/resize-epilogue.frag new file mode 100644 index 00000000..fb4b079a --- /dev/null +++ b/src/render_helpers/shaders/resize-epilogue.frag @@ -0,0 +1,8 @@ +void main() { + vec3 coords_curr_geo = niri_input_to_curr_geo * vec3(niri_v_coords, 1.0); + vec3 size_curr_geo = niri_input_to_curr_geo * vec3(niri_size, 1.0); + + vec4 color = resize_color(coords_curr_geo, size_curr_geo); + + gl_FragColor = color * niri_alpha; +} diff --git a/src/render_helpers/shaders/resize-prelude.frag b/src/render_helpers/shaders/resize-prelude.frag new file mode 100644 index 00000000..95587702 --- /dev/null +++ b/src/render_helpers/shaders/resize-prelude.frag @@ -0,0 +1,21 @@ +#version 100 + +precision mediump float; + +varying vec2 niri_v_coords; +uniform vec2 niri_size; + +uniform mat3 niri_input_to_curr_geo; +uniform mat3 niri_curr_geo_to_prev_geo; +uniform mat3 niri_curr_geo_to_next_geo; + +uniform sampler2D niri_tex_prev; +uniform mat3 niri_geo_to_tex_prev; + +uniform sampler2D niri_tex_next; +uniform mat3 niri_geo_to_tex_next; + +uniform float niri_progress; +uniform float niri_clamped_progress; + +uniform float niri_alpha; diff --git a/src/render_helpers/shaders/resize.frag b/src/render_helpers/shaders/resize.frag index d77b9163..d1ed30fe 100644 --- a/src/render_helpers/shaders/resize.frag +++ b/src/render_helpers/shaders/resize.frag @@ -1,41 +1,10 @@ -#version 100 +vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords_tex_prev = niri_geo_to_tex_prev * coords_curr_geo; + vec4 color_prev = texture2D(niri_tex_prev, coords_tex_prev.st); -precision mediump float; + vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo; + vec4 color_next = texture2D(niri_tex_next, coords_tex_next.st); -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); + vec4 color = mix(color_prev, color_next, niri_clamped_progress); return color; } - -void main() { - vec4 color = crossfade(); - - gl_FragColor = color * alpha; -} - diff --git a/src/render_helpers/shaders/texture.vert b/src/render_helpers/shaders/texture.vert index a59870b8..1a6a4052 100644 --- a/src/render_helpers/shaders/texture.vert +++ b/src/render_helpers/shaders/texture.vert @@ -6,7 +6,7 @@ uniform mat3 tex_matrix; attribute vec2 vert; attribute vec4 vert_position; -varying vec2 v_coords; +varying vec2 niri_v_coords; mat2 scale(vec2 scale_vec){ return mat2( @@ -19,7 +19,7 @@ void main() { vec2 vert_transform_translation = vert_position.xy; vec2 vert_transform_scale = vert_position.zw; vec3 position = vec3(vert * scale(vert_transform_scale) + vert_transform_translation, 1.0); - v_coords = (tex_matrix * position).xy; + niri_v_coords = (tex_matrix * position).xy; gl_Position = vec4(matrix * position, 1.0); } diff --git a/wiki/Configuration:-Animations.md b/wiki/Configuration:-Animations.md index 8c933fdd..440d10fa 100644 --- a/wiki/Configuration:-Animations.md +++ b/wiki/Configuration:-Animations.md @@ -234,31 +234,23 @@ See [this example shader](./examples/resize-custom-shader.frag) for a full docum If a custom shader fails to compile, niri will print a warning and fall back to the default, or previous successfully compiled shader. -> [!NOTE] +> [!WARNING] > > Custom shaders do not have a backwards compatibility guarantee. > I may need to change their interface as I'm developing new features. +Example: resize will show the next (after resize) window texture right away, stretched to the current geometry. + ``` 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; + vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo; + vec4 color = texture2D(niri_tex_next, coords_tex_next.st); + return color; } " } diff --git a/wiki/examples/resize-custom-shader.frag b/wiki/examples/resize-custom-shader.frag index 2ac93738..eb312554 100644 --- a/wiki/examples/resize-custom-shader.frag +++ b/wiki/examples/resize-custom-shader.frag @@ -1,72 +1,87 @@ -#version 100 - -precision mediump float; - -// Coordinates of the current pixel. +// Your shader must contain one function (see the bottom of this file). // -// 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. +// It should not contain any uniform definitions or anything else, as niri +// provides them for you. // -// You very likely want to convert these coordinates to geometry coordinates -// before using them (see below). -varying vec2 v_coords; +// All symbols defined by niri will have a niri_ prefix, so don't use it for +// your own variables and functions. -// Pixel size of the whole area of the shader. -uniform vec2 size; +// The function that you must define looks like this: +vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec4 color = /* ...compute the color... */; + return color; +} -// Matrix that converts the input v_coords into coordinates inside the current +// It takes as input: +// +// * coords_curr_geo: coordinates of the current pixel relative to the current // window geometry. // +// These are homogeneous (the Z component is equal to 1) and scaled in such a +// way that the 0 to 1 coordinates lie within the current window geometry (in +// the middle of a resize). Pixels outside the window geometry will have +// coordinates below 0 or above 1. +// // 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; +// +// The shader runs over an area of unspecified size and location, so you must +// expect and handle coordinates outside the [0, 1] range. The area will be +// large enough to accomodate a crossfade effect. +// +// * size_curr_geo: size of the current window geometry in physical pixels. +// +// It is homogeneous (the Z component is equal to 1). +// +// The function must return the color of the pixel (with premultiplied alpha). +// The pixel color will be further processed by niri (for example, to apply the +// final opacity from window rules). -// Matrix that converts the input v_coords into coordinates inside the next -// (after resize) window geometry. -uniform mat3 input_to_next_geo; +// Now let's go over the uniforms that niri defines. // Previous (before resize) window texture. -uniform sampler2D tex_prev; +uniform sampler2D niri_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; +uniform mat3 niri_geo_to_tex_prev; // Next (after resize) window texture. -uniform sampler2D tex_next; +uniform sampler2D niri_tex_next; // Matrix that converts geometry coordinates into the next window texture // coordinates. -uniform mat3 geo_to_tex_next; +uniform mat3 niri_geo_to_tex_next; + + +// Matrix that converts coords_curr_geo into coordinates inside the previous +// (before resize) window geometry. +uniform mat3 niri_curr_geo_to_prev_geo; + +// Matrix that converts coords_curr_geo into coordinates inside the next +// (after resize) window geometry. +uniform mat3 niri_curr_geo_to_next_geo; + // Unclamped progress of the resize. // // Goes from 0 to 1 but may overshoot and oscillate. -uniform float progress; +uniform float niri_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; +uniform float niri_clamped_progress; -// Additional opacity to apply to the final color. -uniform float alpha; +// Now let's look at some examples. You can copy everything below this line +// into your custom-shader to experiment. // 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 solid_gradient(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords = coords_curr_geo; vec4 color = vec4(0.0); // Paint only the area inside the current geometry. @@ -83,35 +98,33 @@ vec4 solid_gradient() { // 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); +vec4 crossfade(vec3 coords_curr_geo, vec3 size_curr_geo) { + // Convert coordinates into the texture space for sampling. + vec3 coords_tex_prev = niri_geo_to_tex_prev * coords_curr_geo; + vec4 color_prev = texture2D(niri_tex_prev, coords_tex_prev.st); - vec3 coords_tex_prev = geo_to_tex_prev * coords_curr_geo; - vec4 color_prev = texture2D(tex_prev, vec2(coords_tex_prev)); + // Convert coordinates into the texture space for sampling. + vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo; + vec4 color_next = texture2D(niri_tex_next, coords_tex_next.st); - 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); + vec4 color = mix(color_prev, color_next, niri_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)); +vec4 stretch_next(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo; + vec4 color = texture2D(niri_tex_next, coords_tex_next.st); 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); +vec4 stretch_or_crop_next(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords_next_geo = niri_curr_geo_to_next_geo * coords_curr_geo; - vec3 coords_stretch = geo_to_tex_next * coords_curr_geo; - vec3 coords_crop = geo_to_tex_next * coords_next_geo; + vec3 coords_stretch = niri_geo_to_tex_next * coords_curr_geo; + vec3 coords_crop = niri_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 @@ -122,7 +135,7 @@ vec4 stretch_or_crop_next() { if (coords_crop.y < coords_stretch.y) coords.y = coords_crop.y; - vec4 color = texture2D(tex_next, vec2(coords)); + vec4 color = texture2D(niri_tex_next, coords.st); // 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 @@ -142,11 +155,9 @@ vec4 stretch_or_crop_next() { return color; } -// The main entry point of the shader. -void main() { +// This is the function that you must define. +vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) { // You can pick one of the example functions or write your own. - vec4 color = stretch_or_crop_next(); - - gl_FragColor = color * alpha; + return stretch_or_crop_next(coords_curr_geo, size_curr_geo); } |
