use std::ffi::CStr; use std::fmt; use crossfont::Metrics; use log::info; use alacritty_terminal::index::Point; use alacritty_terminal::term::cell::Flags; use alacritty_terminal::term::color::Rgb; use alacritty_terminal::term::SizeInfo; use crate::display::content::RenderableCell; use crate::gl; use crate::renderer::rects::{RectRenderer, RenderRect}; use crate::renderer::shader::ShaderError; pub mod rects; mod shader; mod text; pub use text::{GlyphCache, LoaderApi}; use shader::ShaderVersion; use text::{Gles2Renderer, Glsl3Renderer, TextRenderer}; macro_rules! cstr { ($s:literal) => { // This can be optimized into an no-op with pre-allocated NUL-terminated bytes. unsafe { std::ffi::CStr::from_ptr(concat!($s, "\0").as_ptr().cast()) } }; } pub(crate) use cstr; #[derive(Debug)] pub enum Error { /// Shader error. Shader(ShaderError), } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Error::Shader(err) => err.source(), } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::Shader(err) => { write!(f, "There was an error initializing the shaders: {}", err) }, } } } impl From for Error { fn from(val: ShaderError) -> Self { Error::Shader(val) } } #[derive(Debug)] enum TextRendererProvider { Gles2(Gles2Renderer), Glsl3(Glsl3Renderer), } #[derive(Debug)] pub struct Renderer { text_renderer: TextRendererProvider, rect_renderer: RectRenderer, } impl Renderer { /// Create a new renderer. /// /// This will automatically pick between the GLES2 and GLSL3 renderer based on the GPU's /// supported OpenGL version. pub fn new() -> Result { let (version, renderer) = unsafe { let renderer = CStr::from_ptr(gl::GetString(gl::RENDERER) as *mut _); let version = CStr::from_ptr(gl::GetString(gl::SHADING_LANGUAGE_VERSION) as *mut _); (version.to_string_lossy(), renderer.to_string_lossy()) }; info!("Running on {}", renderer); let (text_renderer, rect_renderer) = if version.as_ref() >= "3.3" { let text_renderer = TextRendererProvider::Glsl3(Glsl3Renderer::new()?); let rect_renderer = RectRenderer::new(ShaderVersion::Glsl3)?; (text_renderer, rect_renderer) } else { let text_renderer = TextRendererProvider::Gles2(Gles2Renderer::new()?); let rect_renderer = RectRenderer::new(ShaderVersion::Gles2)?; (text_renderer, rect_renderer) }; Ok(Self { text_renderer, rect_renderer }) } pub fn draw_cells>( &mut self, size_info: &SizeInfo, glyph_cache: &mut GlyphCache, cells: I, ) { match &mut self.text_renderer { TextRendererProvider::Gles2(renderer) => { renderer.draw_cells(size_info, glyph_cache, cells) }, TextRendererProvider::Glsl3(renderer) => { renderer.draw_cells(size_info, glyph_cache, cells) }, } } /// Draw a string in a variable location. Used for printing the render timer, warnings and /// errors. pub fn draw_string( &mut self, point: Point, fg: Rgb, bg: Rgb, string: &str, size_info: &SizeInfo, glyph_cache: &mut GlyphCache, ) { let cells = string.chars().enumerate().map(|(i, character)| RenderableCell { point: Point::new(point.line, point.column + i), character, zerowidth: None, flags: Flags::empty(), bg_alpha: 1.0, fg, bg, }); self.draw_cells(size_info, glyph_cache, cells); } pub fn with_loader(&mut self, func: F) -> T where F: FnOnce(LoaderApi<'_>) -> T, { match &mut self.text_renderer { TextRendererProvider::Gles2(renderer) => renderer.with_loader(func), TextRendererProvider::Glsl3(renderer) => renderer.with_loader(func), } } /// Draw all rectangles simultaneously to prevent excessive program swaps. pub fn draw_rects(&mut self, size_info: &SizeInfo, metrics: &Metrics, rects: Vec) { if rects.is_empty() { return; } // Prepare rect rendering state. unsafe { // Remove padding from viewport. gl::Viewport(0, 0, size_info.width() as i32, size_info.height() as i32); gl::BlendFuncSeparate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, gl::SRC_ALPHA, gl::ONE); } self.rect_renderer.draw(size_info, metrics, rects); // Activate regular state again. unsafe { // Reset blending strategy. gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR); // Restore viewport with padding. self.set_viewport(size_info); } } /// Fill the window with `color` and `alpha`. pub fn clear(&self, color: Rgb, alpha: f32) { unsafe { gl::ClearColor( (f32::from(color.r) / 255.0).min(1.0) * alpha, (f32::from(color.g) / 255.0).min(1.0) * alpha, (f32::from(color.b) / 255.0).min(1.0) * alpha, alpha, ); gl::Clear(gl::COLOR_BUFFER_BIT); } } #[cfg(not(any(target_os = "macos", windows)))] pub fn finish(&self) { unsafe { gl::Finish(); } } /// Set the viewport for cell rendering. #[inline] pub fn set_viewport(&self, size: &SizeInfo) { unsafe { gl::Viewport( size.padding_x() as i32, size.padding_y() as i32, size.width() as i32 - 2 * size.padding_x() as i32, size.height() as i32 - 2 * size.padding_y() as i32, ); } } /// Resize the renderer. pub fn resize(&self, size_info: &SizeInfo) { self.set_viewport(size_info); match &self.text_renderer { TextRendererProvider::Gles2(renderer) => renderer.resize(size_info), TextRendererProvider::Glsl3(renderer) => renderer.resize(size_info), } } }