summaryrefslogtreecommitdiffstats
path: root/src/renderer/mod.rs
diff options
context:
space:
mode:
authorTheodore Dubois <tblodt@icloud.com>2019-04-28 06:24:58 -0700
committerChristian Duerr <chrisduerr@users.noreply.github.com>2019-04-28 13:24:58 +0000
commitdbd8538762ef8968a493e1bf996e8693479ca783 (patch)
tree32ac2a6a5e01238a272d4ba534551d2e42903c7a /src/renderer/mod.rs
parent9c6d12ea2c863ba76015bdedc00db13b7307725a (diff)
Split alacritty into a separate crates
The crate containing the entry point is called alacritty, and the crate containing everything else is called alacritty_terminal.
Diffstat (limited to 'src/renderer/mod.rs')
-rw-r--r--src/renderer/mod.rs1625
1 files changed, 0 insertions, 1625 deletions
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs
deleted file mode 100644
index c0e3081d..00000000
--- a/src/renderer/mod.rs
+++ /dev/null
@@ -1,1625 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-use std::collections::HashMap;
-use std::fs::File;
-use std::hash::BuildHasherDefault;
-use std::io::{self, Read};
-use std::mem::size_of;
-use std::path::PathBuf;
-use std::ptr;
-use std::sync::mpsc;
-use std::time::Duration;
-
-use fnv::FnvHasher;
-use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer};
-use glutin::dpi::PhysicalSize;
-use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
-
-use crate::ansi::CursorStyle;
-use crate::config::{self, Config, Delta};
-use crate::gl;
-use crate::gl::types::*;
-use crate::index::{Column, Line};
-use crate::renderer::rects::{Rect, Rects};
-use crate::term::color::Rgb;
-use crate::term::{self, cell, RenderableCell, RenderableCellContent};
-
-pub mod rects;
-
-// Shader paths for live reload
-static TEXT_SHADER_F_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl");
-static TEXT_SHADER_V_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl");
-static RECT_SHADER_F_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/rect.f.glsl");
-static RECT_SHADER_V_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/rect.v.glsl");
-
-// Shader source which is used when live-shader-reload feature is disable
-static TEXT_SHADER_F: &'static str =
- include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl"));
-static TEXT_SHADER_V: &'static str =
- include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl"));
-static RECT_SHADER_F: &'static str =
- include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/rect.f.glsl"));
-static RECT_SHADER_V: &'static str =
- include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/rect.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);
-}
-
-enum Msg {
- ShaderReload,
-}
-
-#[derive(Debug)]
-pub enum Error {
- ShaderCreation(ShaderCreationError),
-}
-
-impl ::std::error::Error for Error {
- fn cause(&self) -> Option<&dyn (::std::error::Error)> {
- match *self {
- Error::ShaderCreation(ref err) => Some(err),
- }
- }
-
- fn description(&self) -> &str {
- match *self {
- Error::ShaderCreation(ref err) => err.description(),
- }
- }
-}
-
-impl ::std::fmt::Display for Error {
- fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
- match *self {
- Error::ShaderCreation(ref err) => {
- write!(f, "There was an error initializing the shaders: {}", err)
- },
- }
- }
-}
-
-impl From<ShaderCreationError> for Error {
- fn from(val: ShaderCreationError) -> Error {
- Error::ShaderCreation(val)
- }
-}
-
-/// Text drawing program
-///
-/// Uniforms are prefixed with "u", and vertex attributes are prefixed with "a".
-#[derive(Debug)]
-pub struct TextShaderProgram {
- // Program id
- id: GLuint,
-
- /// 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,
-}
-
-/// Rectangle drawing program
-///
-/// Uniforms are prefixed with "u"
-#[derive(Debug)]
-pub struct RectShaderProgram {
- // Program id
- id: GLuint,
- /// Rectangle color
- u_color: GLint,
-}
-
-#[derive(Copy, Debug, Clone)]
-pub struct Glyph {
- tex_id: GLuint,
- top: f32,
- left: f32,
- width: f32,
- height: f32,
- uv_bot: f32,
- uv_left: f32,
- uv_width: f32,
- uv_height: f32,
-}
-
-/// 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>>,
-
- /// Cache of buffered cursor glyphs
- cursor_cache: HashMap<CursorStyle, Glyph, BuildHasherDefault<FnvHasher>>,
-
- /// Rasterizer for loading new glyphs
- rasterizer: Rasterizer,
-
- /// regular font
- font_key: FontKey,
-
- /// italic font
- italic_key: FontKey,
-
- /// bold font
- bold_key: FontKey,
-
- /// font size
- font_size: font::Size,
-
- /// glyph offset
- glyph_offset: Delta<i8>,
-
- metrics: ::font::Metrics,
-}
-
-impl GlyphCache {
- pub fn new<L>(
- mut rasterizer: Rasterizer,
- font: &config::Font,
- loader: &mut L,
- ) -> Result<GlyphCache, font::Error>
- where
- L: LoadGlyph,
- {
- let (regular, 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, c: 'm', size: font.size() })?;
-
- let metrics = rasterizer.metrics(regular, font.size())?;
-
- let mut cache = GlyphCache {
- cache: HashMap::default(),
- cursor_cache: HashMap::default(),
- rasterizer,
- font_size: font.size(),
- font_key: regular,
- bold_key: bold,
- italic_key: italic,
- glyph_offset: *font.glyph_offset(),
- metrics,
- };
-
- cache.load_glyphs_for_font(regular, loader);
- cache.load_glyphs_for_font(bold, loader);
- cache.load_glyphs_for_font(italic, loader);
-
- Ok(cache)
- }
-
- fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) {
- let size = self.font_size;
- for i in 32u8..=128u8 {
- self.get(GlyphKey { font_key: font, c: i as char, size }, loader);
- }
- }
-
- /// Computes font keys for (Regular, Bold, Italic)
- fn compute_font_keys(
- font: &config::Font,
- rasterizer: &mut Rasterizer,
- ) -> Result<(FontKey, FontKey, FontKey), font::Error> {
- let size = font.size();
-
- // Load regular font
- let regular_desc =
- Self::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal);
-
- let regular = rasterizer.load_font(&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_else(|_| regular)
- }
- };
-
- // Load bold font
- let bold_desc = Self::make_desc(&font.bold(), font::Slant::Normal, font::Weight::Bold);
-
- let bold = load_or_regular(bold_desc);
-
- // Load italic font
- let italic_desc =
- Self::make_desc(&font.italic(), font::Slant::Italic, font::Weight::Normal);
-
- let italic = load_or_regular(italic_desc);
-
- Ok((regular, bold, italic))
- }
-
- fn make_desc(
- desc: &config::FontDescription,
- slant: font::Slant,
- weight: font::Weight,
- ) -> FontDesc {
- let style = if let Some(ref spec) = desc.style {
- font::Style::Specific(spec.to_owned())
- } else {
- font::Style::Description { slant, weight }
- };
- FontDesc::new(desc.family.clone(), style)
- }
-
- pub fn font_metrics(&self) -> font::Metrics {
- self.rasterizer
- .metrics(self.font_key, self.font_size)
- .expect("metrics load since font is loaded at glyph cache creation")
- }
-
- pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph
- where
- L: LoadGlyph,
- {
- let glyph_offset = self.glyph_offset;
- let rasterizer = &mut self.rasterizer;
- let metrics = &self.metrics;
- self.cache.entry(glyph_key).or_insert_with(|| {
- let mut rasterized =
- rasterizer.get_glyph(glyph_key).unwrap_or_else(|_| Default::default());
-
- rasterized.left += i32::from(glyph_offset.x);
- rasterized.top += i32::from(glyph_offset.y);
- rasterized.top -= metrics.descent as i32;
-
- loader.load_glyph(&rasterized)
- })
- }
-
- pub fn update_font_size<L: LoadGlyph>(
- &mut self,
- font: &config::Font,
- size: font::Size,
- dpr: f64,
- loader: &mut L,
- ) -> Result<(), font::Error> {
- // Clear currently cached data in both GL and the registry
- loader.clear();
- self.cache = HashMap::default();
- self.cursor_cache = HashMap::default();
-
- // Update dpi scaling
- self.rasterizer.update_dpr(dpr as f32);
-
- // Recompute font keys
- let font = font.to_owned().with_size(size);
- let (regular, bold, italic) = Self::compute_font_keys(&font, &mut self.rasterizer)?;
-
- self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
- let metrics = self.rasterizer.metrics(regular, size)?;
-
- info!("Font size changed to {:?} with DPR of {}", font.size, dpr);
-
- self.font_size = font.size;
- self.font_key = regular;
- self.bold_key = bold;
- self.italic_key = italic;
- self.metrics = metrics;
-
- self.load_glyphs_for_font(regular, loader);
- self.load_glyphs_for_font(bold, loader);
- self.load_glyphs_for_font(italic, loader);
-
- Ok(())
- }
-
- // Calculate font metrics without access to a glyph cache
- //
- // This should only be used *before* OpenGL is initialized and the glyph cache can be filled.
- pub fn static_metrics(config: &Config, dpr: f32) -> Result<font::Metrics, font::Error> {
- let font = config.font().clone();
-
- let mut rasterizer = font::Rasterizer::new(dpr, config.use_thin_strokes())?;
- let regular_desc =
- GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal);
- let regular = rasterizer.load_font(&regular_desc, font.size())?;
- rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
-
- rasterizer.metrics(regular, font.size())
- }
-}
-
-#[derive(Debug)]
-#[repr(C)]
-struct InstanceData {
- // coords
- col: f32,
- row: f32,
- // glyph offset
- left: f32,
- top: f32,
- // glyph scale
- width: f32,
- height: f32,
- // uv offset
- uv_left: f32,
- uv_bot: f32,
- // uv scale
- uv_width: f32,
- uv_height: f32,
- // color
- r: f32,
- g: f32,
- b: f32,
- // background color
- bg_r: f32,
- bg_g: f32,
- bg_b: f32,
- bg_a: f32,
-}
-
-#[derive(Debug)]
-pub struct QuadRenderer {
- program: TextShaderProgram,
- rect_program: RectShaderProgram,
- vao: GLuint,
- ebo: GLuint,
- vbo_instance: GLuint,
- rect_vao: GLuint,
- rect_vbo: GLuint,
- atlas: Vec<Atlas>,
- current_atlas: usize,
- active_tex: GLuint,
- batch: Batch,
- rx: mpsc::Receiver<Msg>,
-}
-
-#[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 Config,
-}
-
-#[derive(Debug)]
-pub struct LoaderApi<'a> {
- active_tex: &'a mut GLuint,
- atlas: &'a mut Vec<Atlas>,
- current_atlas: &'a mut usize,
-}
-
-#[derive(Debug)]
-pub struct PackedVertex {
- x: f32,
- y: f32,
-}
-
-#[derive(Debug, Default)]
-pub struct Batch {
- tex: GLuint,
- instances: Vec<InstanceData>,
-}
-
-impl Batch {
- #[inline]
- pub fn new() -> Batch {
- Batch { 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;
- }
-
- self.instances.push(InstanceData {
- col: cell.column.0 as f32,
- row: cell.line.0 as f32,
-
- 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: f32::from(cell.fg.r),
- g: f32::from(cell.fg.g),
- b: f32::from(cell.fg.b),
-
- bg_r: f32::from(cell.bg.r),
- bg_g: f32::from(cell.bg.g),
- bg_b: f32::from(cell.bg.b),
- bg_a: cell.bg_alpha,
- });
- }
-
- #[inline]
- pub fn full(&self) -> bool {
- self.capacity() == self.len()
- }
-
- #[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 rect_program = RectShaderProgram::new()?;
-
- let mut vao: GLuint = 0;
- let mut ebo: GLuint = 0;
-
- let mut vbo_instance: GLuint = 0;
-
- let mut rect_vao: GLuint = 0;
- let mut rect_vbo: GLuint = 0;
- let mut rect_ebo: 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,
- );
- // coords
- gl::VertexAttribPointer(
- 0,
- 2,
- gl::FLOAT,
- gl::FALSE,
- size_of::<InstanceData>() as i32,
- ptr::null(),
- );
- gl::EnableVertexAttribArray(0);
- gl::VertexAttribDivisor(0, 1);
- // glyphoffset
- gl::VertexAttribPointer(
- 1,
- 4,
- gl::FLOAT,
- gl::FALSE,
- size_of::<InstanceData>() as i32,
- (2 * size_of::<f32>()) as *const _,
- );
- gl::EnableVertexAttribArray(1);
- gl::VertexAttribDivisor(1, 1);
- // uv
- gl::VertexAttribPointer(
- 2,
- 4,
- gl::FLOAT,
- gl::FALSE,
- size_of::<InstanceData>() as i32,
- (6 * size_of::<f32>()) as *const _,
- );
- gl::EnableVertexAttribArray(2);
- gl::VertexAttribDivisor(2, 1);
- // color
- gl::VertexAttribPointer(
- 3,
- 3,
- gl::FLOAT,
- gl::FALSE,
- size_of::<InstanceData>() as i32,
- (10 * size_of::<f32>()) as *const _,
- );
- gl::EnableVertexAttribArray(3);
- gl::VertexAttribDivisor(3, 1);
- // color
- gl::VertexAttribPointer(
- 4,
- 4,
- gl::FLOAT,
- gl::FALSE,
- size_of::<InstanceData>() as i32,
- (13 * size_of::<f32>()) as *const _,
- );
- gl::EnableVertexAttribArray(4);
- gl::VertexAttribDivisor(4, 1);
-
- // Rectangle setup
- gl::GenVertexArrays(1, &mut rect_vao);
- gl::GenBuffers(1, &mut rect_vbo);
- gl::GenBuffers(1, &mut rect_ebo);
- gl::BindVertexArray(rect_vao);
- let indices: [i32; 6] = [0, 1, 3, 1, 2, 3];
- gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, rect_ebo);
- gl::BufferData(
- gl::ELEMENT_ARRAY_BUFFER,
- (size_of::<i32>() * indices.len()) as _,
- indices.as_ptr() as *const _,
- gl::STATIC_DRAW,
- );
-
- // Cleanup
- gl::BindVertexArray(0);
- gl::BindBuffer(gl::ARRAY_BUFFER, 0);
- gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
- }
-
- let (msg_tx, msg_rx) = mpsc::channel();
-
- if cfg!(feature = "live-shader-reload") {
- ::std::thread::spawn(move || {
- let (tx, rx) = ::std::sync::mpsc::channel();
- // The Duration argument is a debouncing period.
- let mut watcher =
- watcher(tx, Duration::from_millis(10)).expect("create file watcher");
- watcher
- .watch(TEXT_SHADER_F_PATH, RecursiveMode::NonRecursive)
- .expect("watch fragment shader");
- watcher
- .watch(TEXT_SHADER_V_PATH, RecursiveMode::NonRecursive)
- .expect("watch vertex shader");
-
- loop {
- let event = rx.recv().expect("watcher event");
-
- match event {
- DebouncedEvent::Rename(..) => continue,
- DebouncedEvent::Create(_)
- | DebouncedEvent::Write(_)
- | DebouncedEvent::Chmod(_) => {
- msg_tx.send(Msg::ShaderReload).expect("msg send ok");
- },
- _ => {},
- }
- }
- });
- }
-
- let mut renderer = QuadRenderer {
- program,
- rect_program,
- vao,
- ebo,
- vbo_instance,
- rect_vao,
- rect_vbo,
- atlas: Vec::new(),
- current_atlas: 0,
- active_tex: 0,
- batch: Batch::new(),
- rx: msg_rx,
- };
-
- let atlas = Atlas::new(ATLAS_SIZE);
- renderer.atlas.push(atlas);
-
- Ok(renderer)
- }
-
- // Draw all rectangles simultaneously to prevent excessive program swaps
- pub fn draw_rects(
- &mut self,
- config: &Config,
- props: &term::SizeInfo,
- visual_bell_intensity: f64,
- cell_line_rects: Rects,
- ) {
- // Swap to rectangle rendering program
- unsafe {
- // Swap program
- gl::UseProgram(self.rect_program.id);
-
- // Remove padding from viewport
- gl::Viewport(0, 0, props.width as i32, props.height as i32);
-
- // Change blending strategy
- gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
-
- // Setup data and buffers
- gl::BindVertexArray(self.rect_vao);
- gl::BindBuffer(gl::ARRAY_BUFFER, self.rect_vbo);
-
- // Position
- gl::VertexAttribPointer(
- 0,
- 2,
- gl::FLOAT,
- gl::FALSE,
- (size_of::<f32>() * 2) as _,
- ptr::null(),
- );
- gl::EnableVertexAttribArray(0);
- }
-
- // Draw visual bell
- let color = config.visual_bell().color();
- let rect = Rect::new(0., 0., props.width, props.height);
- self.render_rect(&rect, color, visual_bell_intensity as f32, props);
-
- // Draw underlines and strikeouts
- for cell_line_rect in cell_line_rects.rects() {
- self.render_rect(&cell_line_rect.0, cell_line_rect.1, 255., props);
- }
-
- // Deactivate rectangle program again
- unsafe {
- // Reset blending strategy
- gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
-
- // Reset data and buffers
- gl::BindBuffer(gl::ARRAY_BUFFER, 0);
- gl::BindVertexArray(0);
-
- let padding_x = props.padding_x as i32;
- let padding_y = props.padding_y as i32;
- let width = props.width as i32;
- let height = props.height as i32;
- gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y);
-
- // Disable program
- gl::UseProgram(0);
- }
- }
-
- pub fn with_api<F, T>(&mut self, config: &Config, props: &term::SizeInfo, func: F) -> T
- where
- F: FnOnce(RenderApi<'_>) -> T,
- {
- // Flush message queue
- if let Ok(Msg::ShaderReload) = self.rx.try_recv() {
- self.reload_shaders(props);
- }
- while let Ok(_) = self.rx.try_recv() {}
-
- 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 reload_shaders(&mut self, props: &term::SizeInfo) {
- info!("Reloading shaders...");
- let result = (TextShaderProgram::new(), RectShaderProgram::new());
- let (program, rect_program) = match result {
- (Ok(program), Ok(rect_program)) => {
- unsafe {
- gl::UseProgram(program.id);
- program.update_projection(
- props.width,
- props.height,
- props.padding_x,
- props.padding_y,
- );
- gl::UseProgram(0);
- }
-
- info!("... successfully reloaded shaders");
- (program, rect_program)
- },
- (Err(err), _) | (_, Err(err)) => {
- error!("{}", err);
- return;
- },
- };
-
- self.active_tex = 0;
- self.program = program;
- self.rect_program = rect_program;
- }
-
- pub fn resize(&mut self, size: PhysicalSize, padding_x: f32, padding_y: f32) {
- let (width, height): (u32, u32) = size.into();
-
- // viewport
- unsafe {
- let width = width as i32;
- let height = height as i32;
- let padding_x = padding_x as i32;
- let padding_y = padding_y as i32;
- gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y);
-
- // update projection
- gl::UseProgram(self.program.id);
- self.program.update_projection(
- width as f32,
- height as f32,
- padding_x as f32,
- padding_y as f32,
- );
- gl::UseProgram(0);
- }
- }
-
- // Render a rectangle
- //
- // This requires the rectangle program to be activated
- fn render_rect(&mut self, rect: &Rect<f32>, color: Rgb, alpha: f32, size: &term::SizeInfo) {
- // Do nothing when alpha is fully transparent
- if alpha == 0. {
- return;
- }
-
- // Calculate rectangle position
- let center_x = size.width / 2.;
- let center_y = size.height / 2.;
- let x = (rect.x - center_x) / center_x;
- let y = -(rect.y - center_y) / center_y;
- let width = rect.width / center_x;
- let height = rect.height / center_y;
-
- unsafe {
- // Setup vertices
- let vertices: [f32; 8] = [x + width, y, x + width, y - height, x, y - height, x, y];
-
- // Load vertex data into array buffer
- gl::BufferData(
- gl::ARRAY_BUFFER,
- (size_of::<f32>() * vertices.len()) as _,
- vertices.as_ptr() as *const _,
- gl::STATIC_DRAW,
- );
-
- // Color
- self.rect_program.set_color(color, alpha);
-
- // Draw the rectangle
- gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null());
- }
- }
-}
-
-impl<'a> RenderApi<'a> {
- pub fn clear(&self, color: Rgb) {
- let alpha = self.config.background_opacity().get();
- 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);
- }
- }
-
- 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();
- }
-
- /// Render a string in a variable location. Used for printing the render timer, warnings and
- /// errors.
- pub fn render_string(
- &mut self,
- string: &str,
- line: Line,
- glyph_cache: &mut GlyphCache,
- color: Option<Rgb>,
- ) {
- let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0);
- let col = Column(0);
-
- let cells = string
- .chars()
- .enumerate()
- .map(|(i, c)| RenderableCell {
- line,
- column: col + i,
- inner: RenderableCellContent::Chars({
- let mut chars = [' '; cell::MAX_ZEROWIDTH_CHARS + 1];
- chars[0] = c;
- chars
- }),
- bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }),
- fg: Rgb { r: 0, g: 0, b: 0 },
- flags: cell::Flags::empty(),
- bg_alpha,
- })
- .collect::<Vec<_>>();
-
- for cell in cells {
- self.render_cell(cell, glyph_cache);
- }
- }
-
- #[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 render_cell(&mut self, cell: RenderableCell, glyph_cache: &mut GlyphCache) {
- let chars = match cell.inner {
- RenderableCellContent::Cursor((cursor_style, ref raw)) => {
- // Raw cell pixel buffers like cursors don't need to go through font lookup
- let glyph = glyph_cache
- .cursor_cache
- .entry(cursor_style)
- .or_insert_with(|| self.load_glyph(raw));
- self.add_render_item(&cell, &glyph);
- return;
- },
- RenderableCellContent::Chars(chars) => chars,
- };
-
- // Get font key for cell
- // FIXME this is super inefficient.
- let font_key = if cell.flags.contains(cell::Flags::BOLD) {
- glyph_cache.bold_key
- } else if cell.flags.contains(cell::Flags::ITALIC) {
- glyph_cache.italic_key
- } else {
- glyph_cache.font_key
- };
-
- // Don't render text of HIDDEN cells
- let mut chars = if cell.flags.contains(cell::Flags::HIDDEN) {
- [' '; cell::MAX_ZEROWIDTH_CHARS + 1]
- } else {
- chars
- };
-
- // Render tabs as spaces in case the font doesn't support it
- if chars[0] == '\t' {
- chars[0] = ' ';
- }
-
- let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: chars[0] };
-
- // Add cell to batch
- let glyph = glyph_cache.ge