diff options
author | Theodore Dubois <tblodt@icloud.com> | 2019-04-28 06:24:58 -0700 |
---|---|---|
committer | Christian Duerr <chrisduerr@users.noreply.github.com> | 2019-04-28 13:24:58 +0000 |
commit | dbd8538762ef8968a493e1bf996e8693479ca783 (patch) | |
tree | 32ac2a6a5e01238a272d4ba534551d2e42903c7a /src/renderer/mod.rs | |
parent | 9c6d12ea2c863ba76015bdedc00db13b7307725a (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.rs | 1625 |
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(®ular_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(®ular_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 |