diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-04-13 11:07:23 +0400 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-04-13 11:07:23 +0400 |
| commit | 71be19b234d58f4ec447e921633506beb81a52c0 (patch) | |
| tree | a6b69d57a3e2edc9d8dac4c969548f227cbf341a /src/render_helpers | |
| parent | 4fd9300bdb07e90c26df28461f9bd6591c3d1d41 (diff) | |
| download | niri-71be19b234d58f4ec447e921633506beb81a52c0.tar.gz niri-71be19b234d58f4ec447e921633506beb81a52c0.tar.bz2 niri-71be19b234d58f4ec447e921633506beb81a52c0.zip | |
Implement window resize animations
Diffstat (limited to 'src/render_helpers')
| -rw-r--r-- | src/render_helpers/crossfade.rs | 156 | ||||
| -rw-r--r-- | src/render_helpers/mod.rs | 3 | ||||
| -rw-r--r-- | src/render_helpers/primary_gpu_pixel_shader_with_textures.rs | 423 | ||||
| -rw-r--r-- | src/render_helpers/resources.rs | 106 | ||||
| -rw-r--r-- | src/render_helpers/shaders/crossfade.frag | 31 | ||||
| -rw-r--r-- | src/render_helpers/shaders/mod.rs | 24 | ||||
| -rw-r--r-- | src/render_helpers/shaders/texture.vert | 25 |
7 files changed, 767 insertions, 1 deletions
diff --git a/src/render_helpers/crossfade.rs b/src/render_helpers/crossfade.rs new file mode 100644 index 00000000..65f96482 --- /dev/null +++ b/src/render_helpers/crossfade.rs @@ -0,0 +1,156 @@ +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 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; + + // FIXME: cropping this element will mess up the coordinates. + 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, + 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) + } +} diff --git a/src/render_helpers/mod.rs b/src/render_helpers/mod.rs index c445240d..ad3e91b8 100644 --- a/src/render_helpers/mod.rs +++ b/src/render_helpers/mod.rs @@ -17,12 +17,15 @@ 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; +pub mod primary_gpu_pixel_shader_with_textures; pub mod primary_gpu_texture; pub mod render_elements; pub mod renderer; +pub mod resources; pub mod shaders; pub mod surface; diff --git a/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs b/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs new file mode 100644 index 00000000..9f17cfdb --- /dev/null +++ b/src/render_helpers/primary_gpu_pixel_shader_with_textures.rs @@ -0,0 +1,423 @@ +use std::collections::HashMap; +use std::ffi::{CStr, CString}; +use std::rc::Rc; + +use glam::{Mat3, Vec2}; +use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage}; +use smithay::backend::renderer::gles::{ + ffi, link_program, Capability, GlesError, GlesFrame, GlesRenderer, GlesTexture, Uniform, + UniformDesc, UniformName, +}; +use smithay::backend::renderer::utils::CommitCounter; +use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Transform}; + +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. +#[derive(Debug)] +pub struct PrimaryGpuPixelShaderWithTexturesRenderElement { + shader: PixelWithTexturesProgram, + textures: HashMap<String, GlesTexture>, + id: Id, + commit_counter: CommitCounter, + area: Rectangle<i32, Logical>, + opaque_regions: Vec<Rectangle<i32, Logical>>, + alpha: f32, + additional_uniforms: Vec<Uniform<'static>>, + kind: Kind, +} + +#[derive(Debug, Clone)] +pub struct PixelWithTexturesProgram(Rc<PixelWithTexturesProgramInner>); + +#[derive(Debug)] +struct PixelWithTexturesProgramInner { + program: ffi::types::GLuint, + uniform_tex_matrix: ffi::types::GLint, + uniform_matrix: ffi::types::GLint, + uniform_size: ffi::types::GLint, + uniform_alpha: ffi::types::GLint, + attrib_vert: ffi::types::GLint, + attrib_vert_position: ffi::types::GLint, + additional_uniforms: HashMap<String, UniformDesc>, + texture_uniforms: HashMap<String, ffi::types::GLint>, +} + +unsafe fn compile_program( + gl: &ffi::Gles2, + src: &str, + 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)? }; + + 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"); + 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"); + + Ok(PixelWithTexturesProgram(Rc::new( + PixelWithTexturesProgramInner { + program, + uniform_matrix: gl + .GetUniformLocation(program, matrix.as_ptr() as *const ffi::types::GLchar), + uniform_tex_matrix: gl + .GetUniformLocation(program, tex_matrix.as_ptr() as *const ffi::types::GLchar), + uniform_size: gl + .GetUniformLocation(program, size.as_ptr() as *const ffi::types::GLchar), + uniform_alpha: gl + .GetUniformLocation(program, alpha.as_ptr() as *const ffi::types::GLchar), + attrib_vert: gl.GetAttribLocation(program, vert.as_ptr() as *const ffi::types::GLchar), + attrib_vert_position: gl + .GetAttribLocation(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(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(program, name.as_ptr() as *const ffi::types::GLchar); + (name_.to_string(), location) + }) + .collect(), + }, + ))) +} + +impl PixelWithTexturesProgram { + pub fn compile( + renderer: &mut GlesRenderer, + src: &str, + additional_uniforms: &[UniformName<'_>], + texture_uniforms: &[&str], + ) -> Result<Self, GlesError> { + renderer.with_context(move |gl| unsafe { + compile_program(gl, src, additional_uniforms, texture_uniforms) + })? + } +} + +impl PrimaryGpuPixelShaderWithTexturesRenderElement { + pub fn new( + shader: PixelWithTexturesProgram, + textures: HashMap<String, GlesTexture>, + area: Rectangle<i32, Logical>, + opaque_regions: Option<Vec<Rectangle<i32, Logical>>>, + alpha: f32, + additional_uniforms: Vec<Uniform<'_>>, + kind: Kind, + ) -> Self { + Self { + shader, + textures, + id: Id::new(), + commit_counter: CommitCounter::default(), + area, + opaque_regions: opaque_regions.unwrap_or_default(), + alpha, + additional_uniforms: additional_uniforms + .into_iter() + .map(|u| u.into_owned()) + .collect(), + kind, + } + } +} + +impl Element for PrimaryGpuPixelShaderWithTexturesRenderElement { + fn id(&self) -> &Id { + &self.id + } + + fn current_commit(&self) -> CommitCounter { + self.commit_counter + } + + fn src(&self) -> Rectangle<f64, Buffer> { + self.area + .to_f64() + .to_buffer(1.0, Transform::Normal, &self.area.size.to_f64()) + } + + fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> { + self.area.to_physical_precise_round(scale) + } + + fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> { + self.opaque_regions + .iter() + .map(|region| region.to_physical_precise_round(scale)) + .collect() + } + + fn alpha(&self) -> f32 { + 1.0 + } + + fn kind(&self) -> Kind { + self.kind + } +} + +impl RenderElement<GlesRenderer> for PrimaryGpuPixelShaderWithTexturesRenderElement { + fn draw( + &self, + frame: &mut GlesFrame<'_>, + _src: Rectangle<f64, Buffer>, + dest: Rectangle<i32, Physical>, + damage: &[Rectangle<i32, Physical>], + ) -> Result<(), GlesError> { + let frame = frame.as_gles_frame(); + + let Some(resources) = Resources::get(frame) else { + return Ok(()); + }; + let mut resources = resources.borrow_mut(); + + let supports_instancing = frame.capabilities().contains(&Capability::Instancing); + + // prepare the vertices + resources.vertices.clear(); + if supports_instancing { + resources.vertices.extend(damage.iter().flat_map(|rect| { + let dest_size = dest.size; + + let rect_constrained_loc = rect + .loc + .constrain(Rectangle::from_extemities((0, 0), dest_size.to_point())); + let rect_clamped_size = rect.size.clamp( + (0, 0), + (dest_size.to_point() - rect_constrained_loc).to_size(), + ); + + let rect = Rectangle::from_loc_and_size(rect_constrained_loc, rect_clamped_size); + [ + rect.loc.x as f32, + rect.loc.y as f32, + rect.size.w as f32, + rect.size.h as f32, + ] + })); + } else { + resources.vertices.extend(damage.iter().flat_map(|rect| { + let dest_size = dest.size; + + let rect_constrained_loc = rect + .loc + .constrain(Rectangle::from_extemities((0, 0), dest_size.to_point())); + let rect_clamped_size = rect.size.clamp( + (0, 0), + (dest_size.to_point() - rect_constrained_loc).to_size(), + ); + + let rect = Rectangle::from_loc_and_size(rect_constrained_loc, rect_clamped_size); + // Add the 4 f32s per damage rectangle for each of the 6 vertices. + (0..6).flat_map(move |_| { + [ + rect.loc.x as f32, + rect.loc.y as f32, + rect.size.w as f32, + rect.size.h as f32, + ] + }) + })); + } + + if resources.vertices.is_empty() { + return Ok(()); + } + + // dest position and scale + let mut matrix = Mat3::from_translation(Vec2::new(dest.loc.x as f32, dest.loc.y as f32)); + let tex_matrix = Mat3::from_scale(Vec2::new( + (1.0f64 / dest.size.w as f64) as f32, + (1.0f64 / dest.size.h as f64) as f32, + )); + + //apply output transformation + matrix = Mat3::from_cols_array(frame.projection()) * matrix; + + let program = &self.shader.0; + + // render + frame.with_context(move |gl| -> Result<(), GlesError> { + unsafe { + for (i, texture) in self.textures.values().enumerate() { + gl.ActiveTexture(ffi::TEXTURE0 + i as u32); + gl.BindTexture(ffi::TEXTURE_2D, texture.tex_id()); + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32); + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_MAG_FILTER, ffi::LINEAR as i32); + gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_WRAP_S, + ffi::CLAMP_TO_BORDER as i32, + ); + gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_WRAP_T, + ffi::CLAMP_TO_BORDER as i32, + ); + } + + gl.UseProgram(program.program); + + for (i, name) in self.textures.keys().enumerate() { + gl.Uniform1i(program.texture_uniforms[name], i as i32); + } + + gl.UniformMatrix3fv( + program.uniform_matrix, + 1, + ffi::FALSE, + matrix.as_ref().as_ptr(), + ); + gl.UniformMatrix3fv( + program.uniform_tex_matrix, + 1, + ffi::FALSE, + tex_matrix.as_ref().as_ptr(), + ); + gl.Uniform2f(program.uniform_size, dest.size.w as f32, dest.size.h as f32); + gl.Uniform1f(program.uniform_alpha, self.alpha); + + for uniform in &self.additional_uniforms { + let desc = + program + .additional_uniforms + .get(&*uniform.name) + .ok_or_else(|| { + GlesError::UnknownUniform(uniform.name.clone().into_owned()) + })?; + uniform.value.set(gl, desc)?; + } + + gl.EnableVertexAttribArray(program.attrib_vert as u32); + gl.BindBuffer(ffi::ARRAY_BUFFER, resources.vbos[0]); + gl.VertexAttribPointer( + program.attrib_vert as u32, + 2, + ffi::FLOAT, + ffi::FALSE, + 0, + std::ptr::null(), + ); + + // vert_position + gl.EnableVertexAttribArray(program.attrib_vert_position as u32); + gl.BindBuffer(ffi::ARRAY_BUFFER, resources.vbos[1]); + gl.BufferData( + ffi::ARRAY_BUFFER, + (std::mem::size_of::<ffi::types::GLfloat>() * resources.vertices.len()) + as isize, + resources.vertices.as_ptr() as *const _, + ffi::STREAM_DRAW, + ); + + gl.VertexAttribPointer( + program.attrib_vert_position as u32, + 4, + ffi::FLOAT, + ffi::FALSE, + 0, + std::ptr::null(), + ); + + let damage_len = damage.len() as i32; + if supports_instancing { + gl.VertexAttribDivisor(program.attrib_vert as u32, 0); + gl.VertexAttribDivisor(program.attrib_vert_position as u32, 1); + gl.DrawArraysInstanced(ffi::TRIANGLE_STRIP, 0, 4, damage_len); + } else { + // When we have more than 10 rectangles, draw them in batches of 10. + for i in 0..(damage_len - 1) / 10 { + gl.DrawArrays(ffi::TRIANGLES, 0, 6); + + // Set damage pointer to the next 10 rectangles. + let offset = + (i + 1) as usize * 6 * 4 * std::mem::size_of::<ffi::types::GLfloat>(); + gl.VertexAttribPointer( + program.attrib_vert_position as u32, + 4, + ffi::FLOAT, + ffi::FALSE, + 0, + offset as *const _, + ); + } + + // Draw the up to 10 remaining rectangles. + let count = ((damage_len - 1) % 10 + 1) * 6; + gl.DrawArrays(ffi::TRIANGLES, 0, count); + } + + gl.BindBuffer(ffi::ARRAY_BUFFER, 0); + gl.BindTexture(ffi::TEXTURE_2D, 0); + gl.ActiveTexture(ffi::TEXTURE0); + gl.BindTexture(ffi::TEXTURE_2D, 0); + gl.DisableVertexAttribArray(program.attrib_vert as u32); + gl.DisableVertexAttribArray(program.attrib_vert_position as u32); + } + + Ok(()) + })??; + + 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 PrimaryGpuPixelShaderWithTexturesRenderElement +{ + fn draw( + &self, + frame: &mut TtyFrame<'_, '_>, + src: Rectangle<f64, Buffer>, + dst: Rectangle<i32, Physical>, + damage: &[Rectangle<i32, Physical>], + ) -> Result<(), TtyRendererError<'render>> { + let frame = frame.as_gles_frame(); + + RenderElement::<GlesRenderer>::draw(self, 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/resources.rs b/src/render_helpers/resources.rs new file mode 100644 index 00000000..0aa1fed4 --- /dev/null +++ b/src/render_helpers/resources.rs @@ -0,0 +1,106 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use smithay::backend::renderer::gles::{ffi, Capability, GlesError, GlesFrame, GlesRenderer}; + +pub struct Resources { + pub vertices: Vec<f32>, + pub vbos: [ffi::types::GLuint; 2], +} + +static INSTANCED_VERTS: [ffi::types::GLfloat; 8] = [ + 1.0, 0.0, // top right + 0.0, 0.0, // top left + 1.0, 1.0, // bottom right + 0.0, 1.0, // bottom left +]; + +/// Vertices for rendering individual triangles. +const MAX_RECTS_PER_DRAW: usize = 10; +const TRIANGLE_VERTS: [ffi::types::GLfloat; 12 * MAX_RECTS_PER_DRAW] = triangle_verts(); +const fn triangle_verts() -> [ffi::types::GLfloat; 12 * MAX_RECTS_PER_DRAW] { + let mut verts = [0.; 12 * MAX_RECTS_PER_DRAW]; + let mut i = 0; + loop { + // Top Left. + verts[i * 12] = 0.0; + verts[i * 12 + 1] = 0.0; + + // Bottom left. + verts[i * 12 + 2] = 0.0; + verts[i * 12 + 3] = 1.0; + + // Bottom right. + verts[i * 12 + 4] = 1.0; + verts[i * 12 + 5] = 1.0; + + // Top left. + verts[i * 12 + 6] = 0.0; + verts[i * 12 + 7] = 0.0; + + // Bottom right. + verts[i * 12 + 8] = 1.0; + verts[i * 12 + 9] = 1.0; + + // Top right. + verts[i * 12 + 10] = 1.0; + verts[i * 12 + 11] = 0.0; + + i += 1; + if i == MAX_RECTS_PER_DRAW { + break; + } + } + verts +} + +impl Resources { + fn create(renderer: &mut GlesRenderer) -> Result<Self, GlesError> { + let _span = tracy_client::span!("Resources::init"); + + let supports_instancing = renderer.capabilities().contains(&Capability::Instancing); + renderer.with_context(|gl| unsafe { + let vertices: &[ffi::types::GLfloat] = if supports_instancing { + &INSTANCED_VERTS + } else { + &TRIANGLE_VERTS + }; + + let mut vbos = [0; 2]; + gl.GenBuffers(vbos.len() as i32, vbos.as_mut_ptr()); + gl.BindBuffer(ffi::ARRAY_BUFFER, vbos[0]); + gl.BufferData( + ffi::ARRAY_BUFFER, + std::mem::size_of_val(vertices) as isize, + vertices.as_ptr() as *const _, + ffi::STATIC_DRAW, + ); + + gl.BindBuffer(ffi::ARRAY_BUFFER, 0); + + Self { + vertices: vec![], + vbos, + } + }) + } + + pub fn get(frame: &mut GlesFrame) -> Option<Rc<RefCell<Self>>> { + let data = frame.egl_context().user_data(); + data.get().cloned() + } +} + +pub fn init(renderer: &mut GlesRenderer) { + match Resources::create(renderer) { + Ok(resources) => { + let data = renderer.egl_context().user_data(); + if !data.insert_if_missing(|| Rc::new(RefCell::new(resources))) { + error!("resources were already initialized"); + } + } + Err(err) => { + warn!("error creating resources for rendering: {err:?}"); + } + } +} diff --git a/src/render_helpers/shaders/crossfade.frag b/src/render_helpers/shaders/crossfade.frag new file mode 100644 index 00000000..56280ad2 --- /dev/null +++ b/src/render_helpers/shaders/crossfade.frag @@ -0,0 +1,31 @@ +#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; +} + diff --git a/src/render_helpers/shaders/mod.rs b/src/render_helpers/shaders/mod.rs index 1adbaf82..ebe0d08c 100644 --- a/src/render_helpers/shaders/mod.rs +++ b/src/render_helpers/shaders/mod.rs @@ -1,9 +1,11 @@ use smithay::backend::renderer::gles::{GlesPixelProgram, GlesRenderer, UniformName, UniformType}; +use super::primary_gpu_pixel_shader_with_textures::PixelWithTexturesProgram; use super::renderer::NiriRenderer; pub struct Shaders { pub gradient_border: Option<GlesPixelProgram>, + pub crossfade: Option<PixelWithTexturesProgram>, } impl Shaders { @@ -26,7 +28,27 @@ impl Shaders { }) .ok(); - Self { gradient_border } + let crossfade = PixelWithTexturesProgram::compile( + renderer, + include_str!("crossfade.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), + ], + &["tex_from", "tex_to"], + ) + .map_err(|err| { + warn!("error compiling crossfade shader: {err:?}"); + }) + .ok(); + + Self { + gradient_border, + crossfade, + } } pub fn get(renderer: &mut impl NiriRenderer) -> &Self { diff --git a/src/render_helpers/shaders/texture.vert b/src/render_helpers/shaders/texture.vert new file mode 100644 index 00000000..a59870b8 --- /dev/null +++ b/src/render_helpers/shaders/texture.vert @@ -0,0 +1,25 @@ +#version 100 + +uniform mat3 matrix; +uniform mat3 tex_matrix; + +attribute vec2 vert; +attribute vec4 vert_position; + +varying vec2 v_coords; + +mat2 scale(vec2 scale_vec){ + return mat2( + scale_vec.x, 0.0, + 0.0, scale_vec.y + ); +} + +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; + gl_Position = vec4(matrix * position, 1.0); +} + |
