summaryrefslogtreecommitdiffstats
path: root/alacritty/src/renderer/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty/src/renderer/mod.rs')
-rw-r--r--alacritty/src/renderer/mod.rs1221
1 files changed, 91 insertions, 1130 deletions
diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs
index 4aa562ad..cf9ee149 100644
--- a/alacritty/src/renderer/mod.rs
+++ b/alacritty/src/renderer/mod.rs
@@ -1,33 +1,27 @@
-use std::collections::HashMap;
-use std::hash::BuildHasherDefault;
-use std::mem::size_of;
-use std::{fmt, ptr};
+use std::ffi::CStr;
+use std::fmt;
-use bitflags::bitflags;
-use crossfont::{
- BitmapBuffer, Error as RasterizerError, FontDesc, FontKey, GlyphKey, Metrics, Rasterize,
- RasterizedGlyph, Rasterizer, Size, Slant, Style, Weight,
-};
-use fnv::FnvHasher;
-use log::{error, info};
-use unicode_width::UnicodeWidthChar;
+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::config::font::{Font, FontDescription};
-use crate::config::ui_config::{Delta, UiConfig};
use crate::display::content::RenderableCell;
use crate::gl;
-use crate::gl::types::*;
use crate::renderer::rects::{RectRenderer, RenderRect};
-use crate::renderer::shader::{ShaderError, ShaderProgram};
+use crate::renderer::shader::ShaderError;
-pub mod builtin_font;
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) => {
@@ -37,21 +31,6 @@ macro_rules! cstr {
}
pub(crate) use cstr;
-// Shader source.
-static TEXT_SHADER_F: &str = include_str!("../../res/text.f.glsl");
-static TEXT_SHADER_V: &str = include_str!("../../res/text.v.glsl");
-
-/// `LoadGlyph` allows for copying a rasterized glyph into graphics memory.
-pub trait LoadGlyph {
- /// Load the rasterized glyph into GPU memory.
- fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph;
-
- /// Clear any state accumulated from previous loaded glyphs.
- ///
- /// This can, for instance, be used to reset the texture Atlas.
- fn clear(&mut self);
-}
-
#[derive(Debug)]
pub enum Error {
/// Shader error.
@@ -82,588 +61,93 @@ impl From<ShaderError> for Error {
}
}
-/// Text drawing program.
-///
-/// Uniforms are prefixed with "u", and vertex attributes are prefixed with "a".
#[derive(Debug)]
-pub struct TextShaderProgram {
- /// Shader program.
- program: ShaderProgram,
-
- /// Projection scale and offset uniform.
- u_projection: GLint,
-
- /// Cell dimensions (pixels).
- u_cell_dim: GLint,
-
- /// Background pass flag.
- ///
- /// Rendering is split into two passes; 1 for backgrounds, and one for text.
- u_background: GLint,
-}
-
-#[derive(Copy, Clone, Debug)]
-pub struct Glyph {
- tex_id: GLuint,
- multicolor: bool,
- top: i16,
- left: i16,
- width: i16,
- height: i16,
- uv_bot: f32,
- uv_left: f32,
- uv_width: f32,
- uv_height: f32,
+enum TextRendererProvider {
+ Gles2(Gles2Renderer),
+ Glsl3(Glsl3Renderer),
}
-/// Naïve glyph cache.
-///
-/// Currently only keyed by `char`, and thus not possible to hold different
-/// representations of the same code point.
-pub struct GlyphCache {
- /// Cache of buffered glyphs.
- cache: HashMap<GlyphKey, Glyph, BuildHasherDefault<FnvHasher>>,
-
- /// Rasterizer for loading new glyphs.
- rasterizer: Rasterizer,
-
- /// Regular font.
- font_key: FontKey,
-
- /// Bold font.
- bold_key: FontKey,
-
- /// Italic font.
- italic_key: FontKey,
-
- /// Bold italic font.
- bold_italic_key: FontKey,
-
- /// Font size.
- font_size: crossfont::Size,
-
- /// Font offset.
- font_offset: Delta<i8>,
-
- /// Glyph offset.
- glyph_offset: Delta<i8>,
-
- /// Font metrics.
- metrics: Metrics,
-
- /// Whether to use the built-in font for box drawing characters.
- builtin_box_drawing: bool,
+#[derive(Debug)]
+pub struct Renderer {
+ text_renderer: TextRendererProvider,
+ rect_renderer: RectRenderer,
}
-impl GlyphCache {
- pub fn new(mut rasterizer: Rasterizer, font: &Font) -> Result<GlyphCache, crossfont::Error> {
- let (regular, bold, italic, bold_italic) = Self::compute_font_keys(font, &mut rasterizer)?;
-
- // Need to load at least one glyph for the face before calling metrics.
- // The glyph requested here ('m' at the time of writing) has no special
- // meaning.
- rasterizer.get_glyph(GlyphKey { font_key: regular, character: 'm', size: font.size() })?;
-
- let metrics = rasterizer.metrics(regular, font.size())?;
-
- Ok(Self {
- cache: HashMap::default(),
- rasterizer,
- font_size: font.size(),
- font_key: regular,
- bold_key: bold,
- italic_key: italic,
- bold_italic_key: bold_italic,
- font_offset: font.offset,
- glyph_offset: font.glyph_offset,
- metrics,
- builtin_box_drawing: font.builtin_box_drawing,
- })
- }
-
- fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) {
- let size = self.font_size;
-
- // Cache all ascii characters.
- for i in 32u8..=126u8 {
- self.get(GlyphKey { font_key: font, character: i as char, size }, loader, true);
- }
- }
-
- /// Computes font keys for (Regular, Bold, Italic, Bold Italic).
- fn compute_font_keys(
- font: &Font,
- rasterizer: &mut Rasterizer,
- ) -> Result<(FontKey, FontKey, FontKey, FontKey), crossfont::Error> {
- let size = font.size();
-
- // Load regular font.
- let regular_desc = Self::make_desc(font.normal(), Slant::Normal, Weight::Normal);
-
- let regular = Self::load_regular_font(rasterizer, &regular_desc, size)?;
-
- // Helper to load a description if it is not the `regular_desc`.
- let mut load_or_regular = |desc: FontDesc| {
- if desc == regular_desc {
- regular
- } else {
- rasterizer.load_font(&desc, size).unwrap_or(regular)
- }
- };
-
- // Load bold font.
- let bold_desc = Self::make_desc(&font.bold(), Slant::Normal, Weight::Bold);
-
- let bold = load_or_regular(bold_desc);
-
- // Load italic font.
- let italic_desc = Self::make_desc(&font.italic(), Slant::Italic, Weight::Normal);
-
- let italic = load_or_regular(italic_desc);
-
- // Load bold italic font.
- let bold_italic_desc = Self::make_desc(&font.bold_italic(), Slant::Italic, Weight::Bold);
-
- let bold_italic = load_or_regular(bold_italic_desc);
-
- Ok((regular, bold, italic, bold_italic))
- }
-
- fn load_regular_font(
- rasterizer: &mut Rasterizer,
- description: &FontDesc,
- size: Size,
- ) -> Result<FontKey, crossfont::Error> {
- match rasterizer.load_font(description, size) {
- Ok(font) => Ok(font),
- Err(err) => {
- error!("{}", err);
-
- let fallback_desc =
- Self::make_desc(Font::default().normal(), Slant::Normal, Weight::Normal);
- rasterizer.load_font(&fallback_desc, size)
- },
- }
- }
-
- fn make_desc(desc: &FontDescription, slant: Slant, weight: Weight) -> FontDesc {
- let style = if let Some(ref spec) = desc.style {
- Style::Specific(spec.to_owned())
- } else {
- Style::Description { slant, weight }
- };
- FontDesc::new(desc.family.clone(), style)
- }
-
- /// Get a glyph from the font.
+impl Renderer {
+ /// Create a new renderer.
///
- /// If the glyph has never been loaded before, it will be rasterized and inserted into the
- /// cache.
- ///
- /// # Errors
- ///
- /// This will fail when the glyph could not be rasterized. Usually this is due to the glyph
- /// not being present in any font.
- fn get<L>(&mut self, glyph_key: GlyphKey, loader: &mut L, show_missing: bool) -> Glyph
- where
- L: LoadGlyph,
- {
- // Try to load glyph from cache.
- if let Some(glyph) = self.cache.get(&glyph_key) {
- return *glyph;
+ /// This will automatically pick between the GLES2 and GLSL3 renderer based on the GPU's
+ /// supported OpenGL version.
+ pub fn new() -> Result<Self, Error> {
+ 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())
};
- // Rasterize the glyph using the built-in font for special characters or the user's font
- // for everything else.
- let rasterized = self
- .builtin_box_drawing
- .then(|| {
- builtin_font::builtin_glyph(
- glyph_key.character,
- &self.metrics,
- &self.font_offset,
- &self.glyph_offset,
- )
- })
- .flatten()
- .map_or_else(|| self.rasterizer.get_glyph(glyph_key), Ok);
-
- let glyph = match rasterized {
- Ok(rasterized) => self.load_glyph(loader, rasterized),
- // Load fallback glyph.
- Err(RasterizerError::MissingGlyph(rasterized)) if show_missing => {
- // Use `\0` as "missing" glyph to cache it only once.
- let missing_key = GlyphKey { character: '\0', ..glyph_key };
- if let Some(glyph) = self.cache.get(&missing_key) {
- *glyph
- } else {
- // If no missing glyph was loaded yet, insert it as `\0`.
- let glyph = self.load_glyph(loader, rasterized);
- self.cache.insert(missing_key, glyph);
+ info!("Running on {}", renderer);
- glyph
- }
- },
- Err(_) => self.load_glyph(loader, Default::default()),
+ 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)
};
- // Cache rasterized glyph.
- *self.cache.entry(glyph_key).or_insert(glyph)
+ Ok(Self { text_renderer, rect_renderer })
}
- /// Load glyph into the atlas.
- ///
- /// This will apply all transforms defined for the glyph cache to the rasterized glyph before
- /// insertion.
- fn load_glyph<L>(&self, loader: &mut L, mut glyph: RasterizedGlyph) -> Glyph
- where
- L: LoadGlyph,
- {
- glyph.left += i32::from(self.glyph_offset.x);
- glyph.top += i32::from(self.glyph_offset.y);
- glyph.top -= self.metrics.descent as i32;
-
- // The metrics of zero-width characters are based on rendering
- // the character after the current cell, with the anchor at the
- // right side of the preceding character. Since we render the
- // zero-width characters inside the preceding character, the
- // anchor has been moved to the right by one cell.
- if glyph.character.width() == Some(0) {
- glyph.left += self.metrics.average_advance as i32;
+ pub fn draw_cells<I: Iterator<Item = RenderableCell>>(
+ &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)
+ },
}
-
- // Add glyph to cache.
- loader.load_glyph(&glyph)
- }
-
- /// Clear currently cached data in both GL and the registry.
- pub fn clear_glyph_cache<L: LoadGlyph>(&mut self, loader: &mut L) {
- loader.clear();
- self.cache = HashMap::default();
-
- self.load_common_glyphs(loader);
}
- pub fn update_font_size<L: LoadGlyph>(
+ /// Draw a string in a variable location. Used for printing the render timer, warnings and
+ /// errors.
+ pub fn draw_string(
&mut self,
- font: &Font,
- scale_factor: f64,
- loader: &mut L,
- ) -> Result<(), crossfont::Error> {
- // Update dpi scaling.
- self.rasterizer.update_dpr(scale_factor as f32);
- self.font_offset = font.offset;
-
- // Recompute font keys.
- let (regular, bold, italic, bold_italic) =
- Self::compute_font_keys(font, &mut self.rasterizer)?;
-
- self.rasterizer.get_glyph(GlyphKey {
- font_key: regular,
- character: 'm',
- size: font.size(),
- })?;
- let metrics = self.rasterizer.metrics(regular, font.size())?;
-
- info!("Font size changed to {:?} with scale factor of {}", font.size(), scale_factor);
-
- self.font_size = font.size();
- self.font_key = regular;
- self.bold_key = bold;
- self.italic_key = italic;
- self.bold_italic_key = bold_italic;
- self.metrics = metrics;
- self.builtin_box_drawing = font.builtin_box_drawing;
-
- self.clear_glyph_cache(loader);
-
- Ok(())
- }
-
- pub fn font_metrics(&self) -> crossfont::Metrics {
- self.metrics
- }
-
- /// Prefetch glyphs that are almost guaranteed to be loaded anyways.
- pub fn load_common_glyphs<L: LoadGlyph>(&mut self, loader: &mut L) {
- self.load_glyphs_for_font(self.font_key, loader);
- self.load_glyphs_for_font(self.bold_key, loader);
- self.load_glyphs_for_font(self.italic_key, loader);
- self.load_glyphs_for_font(self.bold_italic_key, loader);
- }
-}
-
-// NOTE: These flags must be in sync with their usage in the text.*.glsl shaders.
-bitflags! {
- #[repr(C)]
- struct RenderingGlyphFlags: u8 {
- const WIDE_CHAR = 0b0000_0001;
- const COLORED = 0b0000_0010;
- }
-}
-
-#[derive(Debug)]
-#[repr(C)]
-struct InstanceData {
- // Coords.
- col: u16,
- row: u16,
-
- // Glyph offset.
- left: i16,
- top: i16,
-
- // Glyph size.
- width: i16,
- height: i16,
-
- // UV offset.
- uv_left: f32,
- uv_bot: f32,
-
- // uv scale.
- uv_width: f32,
- uv_height: f32,
-
- // Color.
- r: u8,
- g: u8,
- b: u8,
-
- // Cell flags like multicolor or fullwidth character.
- cell_flags: RenderingGlyphFlags,
-
- // Background color.
- bg_r: u8,
- bg_g: u8,
- bg_b: u8,
- bg_a: u8,
-}
-
-#[derive(Debug)]
-pub struct QuadRenderer {
- program: TextShaderProgram,
- vao: GLuint,
- ebo: GLuint,
- vbo_instance: GLuint,
- atlas: Vec<Atlas>,
- current_atlas: usize,
- active_tex: GLuint,
- batch: Batch,
-
- rect_renderer: RectRenderer,
-}
-
-#[derive(Debug)]
-pub struct RenderApi<'a> {
- active_tex: &'a mut GLuint,
- batch: &'a mut Batch,
- atlas: &'a mut Vec<Atlas>,
- current_atlas: &'a mut usize,
- program: &'a mut TextShaderProgram,
- config: &'a UiConfig,
-}
-
-#[derive(Debug)]
-pub struct LoaderApi<'a> {
- active_tex: &'a mut GLuint,
- atlas: &'a mut Vec<Atlas>,
- current_atlas: &'a mut usize,
-}
-
-#[derive(Debug, Default)]
-pub struct Batch {
- tex: GLuint,
- instances: Vec<InstanceData>,
-}
-
-impl Batch {
- #[inline]
- pub fn new() -> Self {
- Self { tex: 0, instances: Vec::with_capacity(BATCH_MAX) }
- }
-
- pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
- if self.is_empty() {
- self.tex = glyph.tex_id;
- }
-
- let mut cell_flags = RenderingGlyphFlags::empty();
- cell_flags.set(RenderingGlyphFlags::COLORED, glyph.multicolor);
- cell_flags.set(RenderingGlyphFlags::WIDE_CHAR, cell.flags.contains(Flags::WIDE_CHAR));
-
- self.instances.push(InstanceData {
- col: cell.point.column.0 as u16,
- row: cell.point.line as u16,
-
- top: glyph.top,
- left: glyph.left,
- width: glyph.width,
- height: glyph.height,
-
- uv_bot: glyph.uv_bot,
- uv_left: glyph.uv_left,
- uv_width: glyph.uv_width,
- uv_height: glyph.uv_height,
-
- r: cell.fg.r,
- g: cell.fg.g,
- b: cell.fg.b,
- cell_flags,
-
- bg_r: cell.bg.r,
- bg_g: cell.bg.g,
- bg_b: cell.bg.b,
- bg_a: (cell.bg_alpha * 255.0) as u8,
+ point: Point<usize>,
+ 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,
});
- }
- #[inline]
- pub fn full(&self) -> bool {
- self.capacity() == self.len()
+ self.draw_cells(size_info, glyph_cache, cells);
}
- #[inline]
- pub fn len(&self) -> usize {
- self.instances.len()
- }
-
- #[inline]
- pub fn capacity(&self) -> usize {
- BATCH_MAX
- }
-
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- #[inline]
- pub fn size(&self) -> usize {
- self.len() * size_of::<InstanceData>()
- }
-
- pub fn clear(&mut self) {
- self.tex = 0;
- self.instances.clear();
- }
-}
-
-/// Maximum items to be drawn in a batch.
-const BATCH_MAX: usize = 0x1_0000;
-const ATLAS_SIZE: i32 = 1024;
-
-impl QuadRenderer {
- pub fn new() -> Result<QuadRenderer, Error> {
- let program = TextShaderProgram::new()?;
-
- let mut vao: GLuint = 0;
- let mut ebo: GLuint = 0;
-
- let mut vbo_instance: GLuint = 0;
-
- unsafe {
- gl::Enable(gl::BLEND);
- gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
- gl::Enable(gl::MULTISAMPLE);
-
- // Disable depth mask, as the renderer never uses depth tests.
- gl::DepthMask(gl::FALSE);
-
- gl::GenVertexArrays(1, &mut vao);
- gl::GenBuffers(1, &mut ebo);
- gl::GenBuffers(1, &mut vbo_instance);
- gl::BindVertexArray(vao);
-
- // ---------------------
- // Set up element buffer
- // ---------------------
- let indices: [u32; 6] = [0, 1, 3, 1, 2, 3];
-
- gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
- gl::BufferData(
- gl::ELEMENT_ARRAY_BUFFER,
- (6 * size_of::<u32>()) as isize,
- indices.as_ptr() as *const _,
- gl::STATIC_DRAW,
- );
-
- // ----------------------------
- // Setup vertex instance buffer
- // ----------------------------
- gl::BindBuffer(gl::ARRAY_BUFFER, vbo_instance);
- gl::BufferData(
- gl::ARRAY_BUFFER,
- (BATCH_MAX * size_of::<InstanceData>()) as isize,
- ptr::null(),
- gl::STREAM_DRAW,
- );
-
- let mut index = 0;
- let mut size = 0;
-
- macro_rules! add_attr {
- ($count:expr, $gl_type:expr, $type:ty) => {
- gl::VertexAttribPointer(
- index,
- $count,
- $gl_type,
- gl::FALSE,
- size_of::<InstanceData>() as i32,
- size as *const _,
- );
- gl::EnableVertexAttribArray(index);
- gl::VertexAttribDivisor(index, 1);
-
- #[allow(unused_assignments)]
- {
- size += $count * size_of::<$type>();
- index += 1;
- }
- };
- }
-
- // Coords.
- add_attr!(2, gl::UNSIGNED_SHORT, u16);
-
- // Glyph offset and size.
- add_attr!(4, gl::SHORT, i16);
-
- // UV offset.
- add_attr!(4, gl::FLOAT, f32);
-
- // Color and cell flags.
- //
- // These are packed together because of an OpenGL driver issue on macOS, which caused a
- // `vec3(u8)` text color and a `u8` cell flags to increase the rendering time by a
- // huge margin.
- add_attr!(4, gl::UNSIGNED_BYTE, u8);
-
- // Background color.
- add_attr!(4, gl::UNSIGNED_BYTE, u8);
-
- // Cleanup.
- gl::BindVertexArray(0);
- gl::BindBuffer(gl::ARRAY_BUFFER, 0);
- gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
+ pub fn with_loader<F, T>(&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),
}
-
- let mut renderer = Self {
- program,
- rect_renderer: RectRenderer::new()?,
- vao,
- ebo,
- vbo_instance,
- atlas: Vec::new(),
- current_atlas: 0,
- active_tex: 0,
- batch: Batch::new(),
- };
-
- let atlas = Atlas::new(ATLAS_SIZE);
- renderer.atlas.push(atlas);
-
- Ok(renderer)
}
/// Draw all rectangles simultaneously to prevent excessive program swaps.
@@ -691,99 +175,9 @@ impl QuadRenderer {
}
}
- pub fn with_api<F, T>(&mut self, config: &UiConfig, props: &SizeInfo, func: F) -> T
- where
- F: FnOnce(RenderApi<'_>) -> T,
- {
+ /// Fill the window with `color` and `alpha`.
+ pub fn clear(&self, color: Rgb, alpha: f32) {
unsafe {
- gl::UseProgram(self.program.id());
- self.program.set_term_uniforms(props);
-
- gl::BindVertexArray(self.vao);
- gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
- gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo_instance);
- gl::ActiveTexture(gl::TEXTURE0);
- }
-
- let res = func(RenderApi {
- active_tex: &mut self.active_tex,
- batch: &mut self.batch,
- atlas: &mut self.atlas,
- current_atlas: &mut self.current_atlas,
- program: &mut self.program,
- config,
- });
-
- unsafe {
- gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
- gl::BindBuffer(gl::ARRAY_BUFFER, 0);
- gl::BindVertexArray(0);
-
- gl::UseProgram(0);
- }
-
- res
- }
-
- pub fn with_loader<F, T>(&mut self, func: F) -> T
- where
- F: FnOnce(LoaderApi<'_>) -> T,
- {
- unsafe {
- gl::ActiveTexture(gl::TEXTURE0);
- }
-
- func(LoaderApi {
- active_tex: &mut self.active_tex,
- atlas: &mut self.atlas,
- current_atlas: &mut self.current_atlas,
- })
- }
-
- pub fn resize(&self, size: &SizeInfo) {
- unsafe {
- self.set_viewport(size);
-
- // Update projection.
- gl::UseProgram(self.program.id());
- self.program.update_projection(
- size.width(),
- size.height(),
- size.padding_x(),
- size.padding_y(),
- );
- gl::UseProgram(0);
- }
- }
-
- /// 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,
- );
- }
- }
-}
-
-impl Drop for QuadRenderer {
- fn drop(&mut self) {
- unsafe {
- gl::DeleteBuffers(1, &self.vbo_instance);
- gl::DeleteBuffers(1, &self.ebo);
- gl::DeleteVertexArrays(1, &self.vao);
- }
- }
-}
-
-impl<'a> RenderApi<'a> {
- pub fn clear(&self, color: Rgb) {
- unsafe {
- let alpha = self.config.window_opacity();
gl::ClearColor(
(f32::from(color.r) / 255.0).min(1.0) * alpha,
(f32::from(color.g) / 255.0).min(1.0) * alpha,
@@ -801,458 +195,25 @@ impl<'a> RenderApi<'a> {
}
}
- fn render_batch(&mut self) {
- unsafe {
- gl::BufferSubData(
- gl::ARRAY_BUFFER,
- 0,
- self.batch.size() as isize,
- self.batch.instances.as_ptr() as *const _,
- );
- }
-
- // Bind texture if necessary.
- if *self.active_tex != self.batch.tex {
- unsafe {
- gl::BindTexture(gl::TEXTURE_2D, self.batch.tex);
- }
- *self.active_tex = self.batch.tex;
- }
-
- unsafe {
- self.program.set_background_pass(true);
- gl::DrawElementsInstanced(
- gl::TRIANGLES,
- 6,
- gl::UNSIGNED_INT,
- ptr::null(),
- self.batch.len() as GLsizei,
- );
- self.program.set_background_pass(false);
- gl::DrawElementsInstanced(
- gl::TRIANGLES,
- 6,
- gl::UNSIGNED_INT,
- ptr::null(),
- self.batch.len() as GLsizei,
- );
- }
-
- self.batch.clear();
- }
-
- /// Draw a string in a variable location. Used for printing the render timer, warnings and
- /// errors.
- pub fn draw_string(
- &mut self,
- glyph_cache: &mut GlyphCache,
- point: Point<usize>,
- fg: Rgb,
- bg: Rgb,
- string: &str,
- ) {
- 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,
- })
- .collect::<Vec<_>>();
-
- for cell in cells {
- self.draw_cell(cell, glyph_cache);
- }
- }
-
+ /// Set the viewport for cell rendering.
#[inline]
- fn add_render_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
- // Flush batch if tex changing.
- if !self.batch.is_empty() && self.batch.tex != glyph.tex_id {
- self.render_batch();
- }
-
- self.batch.add_item(cell, glyph);
-
- // Render batch and clear if it's full.
- if self.batch.full() {
- self.render_batch();
- }
- }
-
- pub fn draw_cell(&mut self, mut cell: RenderableCell, glyph_cache: &mut GlyphCache) {
- // Get font key for cell.
- let font_key = match cell.flags & Flags::BOLD_ITALIC {
- Flags::BOLD_ITALIC => glyph_cache.bold_italic_key,
- Flags::ITALIC => glyph_cache.italic_key,
- Flags::BOLD => glyph_cache.bold_key,
- _ => glyph_cache.font_key,
- };
-
- // Ignore hidden cells and render tabs as spaces to prevent font issues.
- let hidden = cell.flags.contains(Flags::HIDDEN);
- if cell.character == '\t' || hidden {
- cell.character = ' ';
- }
-
- let mut glyph_key =
- GlyphKey { font_key, size: glyph_cache.font_size, character: cell.character };
-
- // Add cell to batch.
- let glyph = glyph_cache.get(glyph_key, self, true);
- self.add_render_item(&cell, &glyph);
-
- // Render visible zero-width characters.
- if let Some(zerowidth) = cell.zerowidth.take().filter(|_| !hidden) {
- for character in zerowidth {
- glyph_key.character = character;
- let glyph = glyph_cache.get(glyph_key, self, false);
- self.add_render_item(&cell, &glyph);
- }
- }
- }
-}
-
-/// Load a glyph into a texture atlas.
-///
-/// If the current atlas is full, a new one will be created.
-#[inline]
-fn load_glyph(
- active_tex: &mut GLuint,
- atlas: &mut Vec<Atlas>,
- current_atlas: &mut usize,
- rasterized: &RasterizedGlyph,
-) -> Glyph {
- // At least one atlas is guaranteed to be in the `self.atlas` list; thus
- // the unwrap.
- match atlas[*current_atlas].insert(rasterized, active_tex) {
- Ok(glyph) => glyph,
- Err(AtlasInsertError::Full) => {
- *current_atlas += 1;
- if *current_atlas == atlas.len() {
- let new = Atlas::new(ATLAS_SIZE);
- *active_tex = 0; // Atlas::new binds a texture. Ugh this is sloppy.
- atlas.push(new);
- }
- load_glyph(active_tex, atlas, current_atlas, rasterized)
- },
- Err(AtlasInsertError::GlyphTooLarge) => Glyph {
- tex_id: atlas[*current_atlas].id,
- multicolor: false,
- top: 0,
- left: 0,
- width: 0,
- height: 0,
- uv_bot: 0.,
- uv_left: 0.,
- uv_width: 0.,
- uv_height: 0.,
- },
- }
-}
-
-#[inline]
-fn clear_atlas(atlas: &mut Vec<Atlas>, current_atlas: &mut usize) {
- for atlas in atlas.iter_mut() {
- atlas.clear();
- }
- *current_atlas = 0;
-}
-
-impl<'a> LoadGlyph for LoaderApi<'a> {
- fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph {
- load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized)
- }
-
- fn clear(&mut self) {
- clear_atlas(self.atlas, self.current_atlas)
- }
-}
-
-impl<'a> LoadGlyph for RenderApi<'a> {
- fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph {
- load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized)
- }
-
- fn clear(&mut self) {
- clear_atlas(self.atlas, self.current_atlas)
- }
-}
-
-impl<'a> Drop for RenderApi<'a> {
- fn drop(&mut self) {
- if !self.batch.is_empty() {
- self.render_batch();
- }
- }
-}
-
-impl TextShaderProgram {
- pub fn new() -> Result<TextShaderProgram, Error> {
- let program = ShaderProgram::new(TEXT_SHADER_V, TEXT_SHADER_F)?;
- Ok(Self {
- u_projection: program.get_uniform_location(cstr!("projection"))?,
- u_cell_dim: program.get_uniform_location(cstr!("cellDim"))?,
- u_background: program.get_uniform_location(cstr!("backgroundPass"))?,
- program,
- })
- }
-
- fn id(&self) -> GLuint {
- self.program.id()
- }
-
- fn update_projection(&self, width: f32, height: f32, padding_x: f32, padding_y: f32) {
- // Bounds check.
- if (width as u32) < (2 * padding_x as u32) || (height as u32) < (2 * padding_y as u32) {
- return;
- }
-
- // Compute scale and offset factors, from pixel to ndc space. Y is inverted.
- // [0, width - 2 * padding_x] to [-1, 1]
- // [height - 2 * padding_y, 0] to [-1, 1]
- let scale_x = 2. / (width - 2. * padding_x);
- let scale_y = -2. / (height - 2. * padding_y);
- let offset_x = -1.;
- let offset_y = 1.;
-
- unsafe {
- gl::Uniform4f(self.u_projection, offset_x, offset_y, scale_x, scale_y);
- }
- }
-
- fn set_term_uniforms(&self, props: &SizeInfo) {