diff options
Diffstat (limited to 'alacritty/src/renderer/rects.rs')
-rw-r--r-- | alacritty/src/renderer/rects.rs | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs index 2a727b4c..040e0e4b 100644 --- a/alacritty/src/renderer/rects.rs +++ b/alacritty/src/renderer/rects.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::mem; use crossfont::Metrics; @@ -7,6 +8,10 @@ use alacritty_terminal::term::cell::Flags; use alacritty_terminal::term::color::Rgb; use alacritty_terminal::term::{RenderableCell, SizeInfo}; +use crate::gl; +use crate::gl::types::*; +use crate::renderer; + #[derive(Debug, Copy, Clone)] pub struct RenderRect { pub x: f32, @@ -190,3 +195,199 @@ impl RenderLines { } } } + +/// Shader sources for rect rendering program. +pub static RECT_SHADER_F_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/rect.f.glsl"); +pub static RECT_SHADER_V_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/rect.v.glsl"); +static RECT_SHADER_F: &str = include_str!("../../res/rect.f.glsl"); +static RECT_SHADER_V: &str = include_str!("../../res/rect.v.glsl"); + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +struct Vertex { + // Normalized screen coordinates. + x: f32, + y: f32, + + // Color. + r: u8, + g: u8, + b: u8, + a: u8, +} + +#[derive(Debug)] +pub struct RectRenderer { + // GL buffer objects. + vao: GLuint, + vbo: GLuint, + + program: RectShaderProgram, + + vertices: Vec<Vertex>, +} + +impl RectRenderer { + /// Update program when doing live-shader-reload. + pub fn set_program(&mut self, program: RectShaderProgram) { + self.program = program; + } + + pub fn new() -> Result<Self, renderer::Error> { + let mut vao: GLuint = 0; + let mut vbo: GLuint = 0; + let program = RectShaderProgram::new()?; + + unsafe { + // Allocate buffers. + gl::GenVertexArrays(1, &mut vao); + gl::GenBuffers(1, &mut vbo); + + gl::BindVertexArray(vao); + + // VBO binding is not part of VAO itself, but VBO binding is stored in attributes. + gl::BindBuffer(gl::ARRAY_BUFFER, vbo); + + let mut attribute_offset = 0; + + // Position. + gl::VertexAttribPointer( + 0, + 2, + gl::FLOAT, + gl::FALSE, + mem::size_of::<Vertex>() as i32, + attribute_offset as *const _, + ); + gl::EnableVertexAttribArray(0); + attribute_offset += mem::size_of::<f32>() * 2; + + // Color. + gl::VertexAttribPointer( + 1, + 4, + gl::UNSIGNED_BYTE, + gl::TRUE, + mem::size_of::<Vertex>() as i32, + attribute_offset as *const _, + ); + gl::EnableVertexAttribArray(1); + + // Reset buffer bindings. + gl::BindVertexArray(0); + gl::BindBuffer(gl::ARRAY_BUFFER, 0); + } + + Ok(Self { vao, vbo, program, vertices: Vec::new() }) + } + + pub fn draw(&mut self, size_info: &SizeInfo, rects: Vec<RenderRect>) { + unsafe { + // Bind VAO to enable vertex attribute slots. + gl::BindVertexArray(self.vao); + + // Bind VBO only once for buffer data upload only. + gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); + + gl::UseProgram(self.program.id); + } + + let half_width = size_info.width() / 2.; + let half_height = size_info.height() / 2.; + + // Build rect vertices vector. + self.vertices.clear(); + for rect in &rects { + self.add_rect(half_width, half_height, rect); + } + + unsafe { + // Upload accumulated vertices. + gl::BufferData( + gl::ARRAY_BUFFER, + (self.vertices.len() * mem::size_of::<Vertex>()) as isize, + self.vertices.as_ptr() as *const _, + gl::STREAM_DRAW, + ); + + // Draw all vertices as list of triangles. + gl::DrawArrays(gl::TRIANGLES, 0, self.vertices.len() as i32); + + // Disable program. + gl::UseProgram(0); + + // Reset buffer bindings to nothing. + gl::BindBuffer(gl::ARRAY_BUFFER, 0); + gl::BindVertexArray(0); + } + } + + fn add_rect(&mut self, half_width: f32, half_height: f32, rect: &RenderRect) { + // Calculate rectangle vertices positions in normalized device coordinates. + // NDC range from -1 to +1, with Y pointing up. + let x = rect.x / half_width - 1.0; + let y = -rect.y / half_height + 1.0; + let width = rect.width / half_width; + let height = rect.height / half_height; + let Rgb { r, g, b } = rect.color; + let a = (rect.alpha * 255.) as u8; + + // Make quad vertices. + let quad = [ + Vertex { x, y, r, g, b, a }, + Vertex { x, y: y - height, r, g, b, a }, + Vertex { x: x + width, y, r, g, b, a }, + Vertex { x: x + width, y: y - height, r, g, b, a }, + ]; + + // Append the vertices to form two triangles. + self.vertices.push(quad[0]); + self.vertices.push(quad[1]); + self.vertices.push(quad[2]); + self.vertices.push(quad[2]); + self.vertices.push(quad[3]); + self.vertices.push(quad[1]); + } +} + +/// Rectangle drawing program. +#[derive(Debug)] +pub struct RectShaderProgram { + /// Program id. + id: GLuint, +} + +impl RectShaderProgram { + pub fn new() -> Result<Self, renderer::ShaderCreationError> { + let (vertex_src, fragment_src) = if cfg!(feature = "live-shader-reload") { + (None, None) + } else { + (Some(RECT_SHADER_V), Some(RECT_SHADER_F)) + }; + let vertex_shader = + renderer::create_shader(RECT_SHADER_V_PATH, gl::VERTEX_SHADER, vertex_src)?; + let fragment_shader = + renderer::create_shader(RECT_SHADER_F_PATH, gl::FRAGMENT_SHADER, fragment_src)?; + let program = renderer::create_program(vertex_shader, fragment_shader)?; + + unsafe { + gl::DeleteShader(fragment_shader); + gl::DeleteShader(vertex_shader); + gl::UseProgram(program); + } + + let shader = Self { id: program }; + + unsafe { gl::UseProgram(0) } + + Ok(shader) + } +} + +impl Drop for RectShaderProgram { + fn drop(&mut self) { + unsafe { + gl::DeleteProgram(self.id); + } + } +} |