aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src
diff options
context:
space:
mode:
authorVasily Khoruzhick <vasily.khoruzhick@gmail.com>2022-06-08 02:02:57 -0700
committerGitHub <noreply@github.com>2022-06-08 12:02:57 +0300
commit6dc670cde0c136e28c71d4ebe67c5c8bb9df65b1 (patch)
tree31ca11c0e1784d6f608286d7f883406231b74068 /alacritty/src
parent29b1ff59e29cccd69bee349ca6937082dee718f6 (diff)
downloadr-alacritty-6dc670cde0c136e28c71d4ebe67c5c8bb9df65b1.tar.gz
r-alacritty-6dc670cde0c136e28c71d4ebe67c5c8bb9df65b1.tar.bz2
r-alacritty-6dc670cde0c136e28c71d4ebe67c5c8bb9df65b1.zip
Support dual source blending in GLES2 renderer
GLES2 has GL_EXT_blend_func_extended extension that enables dual-source blending, so essentially we can reuse fragment shader from GLSL3 renderer and do 1 rendering pass instead of 3 for the text. Co-authored-by: Kirill Chibisov <contact@kchibisov.com> Co-authored-by: Christian Duerr <contact@christianduerr.com>
Diffstat (limited to 'alacritty/src')
-rw-r--r--alacritty/src/renderer/mod.rs39
-rw-r--r--alacritty/src/renderer/text/gles2.rs81
-rw-r--r--alacritty/src/renderer/text/glsl3.rs25
-rw-r--r--alacritty/src/renderer/text/mod.rs20
4 files changed, 122 insertions, 43 deletions
diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs
index 0446379e..eb0012bd 100644
--- a/alacritty/src/renderer/mod.rs
+++ b/alacritty/src/renderer/mod.rs
@@ -1,8 +1,10 @@
+use std::collections::HashSet;
use std::ffi::CStr;
use std::fmt;
use crossfont::Metrics;
use log::info;
+use once_cell::sync::OnceCell;
use alacritty_terminal::index::Point;
use alacritty_terminal::term::cell::Flags;
@@ -218,3 +220,40 @@ impl Renderer {
}
}
}
+
+struct GlExtensions;
+
+impl GlExtensions {
+ /// Check if the given `extension` is supported.
+ ///
+ /// This function will lazyly load OpenGL extensions.
+ fn contains(extension: &str) -> bool {
+ static OPENGL_EXTENSIONS: OnceCell<HashSet<&'static str>> = OnceCell::new();
+
+ OPENGL_EXTENSIONS.get_or_init(Self::load_extensions).contains(extension)
+ }
+
+ /// Load available OpenGL extensions.
+ fn load_extensions() -> HashSet<&'static str> {
+ unsafe {
+ let extensions = gl::GetString(gl::EXTENSIONS);
+
+ if extensions.is_null() {
+ let mut extensions_number = 0;
+ gl::GetIntegerv(gl::NUM_EXTENSIONS, &mut extensions_number);
+
+ (0..extensions_number as gl::types::GLuint)
+ .flat_map(|i| {
+ let extension = CStr::from_ptr(gl::GetStringi(gl::EXTENSIONS, i) as *mut _);
+ extension.to_str()
+ })
+ .collect()
+ } else {
+ match CStr::from_ptr(extensions as *mut _).to_str() {
+ Ok(ext) => ext.split_whitespace().collect(),
+ Err(_) => HashSet::new(),
+ }
+ }
+ }
+ }
+}
diff --git a/alacritty/src/renderer/text/gles2.rs b/alacritty/src/renderer/text/gles2.rs
index 4d8347bf..32aaa173 100644
--- a/alacritty/src/renderer/text/gles2.rs
+++ b/alacritty/src/renderer/text/gles2.rs
@@ -11,11 +11,12 @@ use crate::display::SizeInfo;
use crate::gl;
use crate::gl::types::*;
use crate::renderer::shader::{ShaderProgram, ShaderVersion};
-use crate::renderer::{cstr, Error};
+use crate::renderer::{cstr, Error, GlExtensions};
use super::atlas::{Atlas, ATLAS_SIZE};
use super::{
- Glyph, LoadGlyph, LoaderApi, TextRenderApi, TextRenderBatch, TextRenderer, TextShader,
+ glsl3, Glyph, LoadGlyph, LoaderApi, RenderingGlyphFlags, RenderingPass, TextRenderApi,
+ TextRenderBatch, TextRenderer, TextShader,
};
// Shader source.
@@ -32,13 +33,21 @@ pub struct Gles2Renderer {
batch: Batch,
current_atlas: usize,
active_tex: GLuint,
+ dual_source_blending: bool,
}
impl Gles2Renderer {
pub fn new() -> Result<Self, Error> {
info!("Using OpenGL ES 2.0 renderer");
- let program = TextShaderProgram::new(ShaderVersion::Gles2)?;
+ let dual_source_blending = GlExtensions::contains("GL_EXT_blend_func_extended")
+ || GlExtensions::contains("GL_ARB_blend_func_extended");
+
+ if dual_source_blending {
+ info!("Using dual source blending");
+ }
+
+ let program = TextShaderProgram::new(ShaderVersion::Gles2, dual_source_blending)?;
let mut vao: GLuint = 0;
let mut vbo: GLuint = 0;
let mut ebo: GLuint = 0;
@@ -139,6 +148,7 @@ impl Gles2Renderer {
batch: Batch::new(),
current_atlas: 0,
active_tex: 0,
+ dual_source_blending,
})
}
}
@@ -180,6 +190,7 @@ impl<'a> TextRenderer<'a> for Gles2Renderer {
atlas: &mut self.atlas,
current_atlas: &mut self.current_atlas,
program: &mut self.program,
+ dual_source_blending: self.dual_source_blending,
});
unsafe {
@@ -205,7 +216,7 @@ impl<'a> TextRenderer<'a> for Gles2Renderer {
/// Maximum items to be drawn in a batch.
///
/// We use the closest number to `u16::MAX` dividable by 4 (amount of vertices we push for a glyph),
-/// since it's the maximum possible index in `glDrawElements` in gles2.
+/// since it's the maximum possible index in `glDrawElements` in GLES2.
const BATCH_MAX: usize = (u16::MAX - u16::MAX % 4) as usize;
#[derive(Debug)]
@@ -269,7 +280,12 @@ impl TextRenderBatch for Batch {
let glyph_x = cell.point.column.0 as i16 * size_info.cell_width() as i16 + glyph.left;
let glyph_y = (cell.point.line + 1) as i16 * size_info.cell_height() as i16 - glyph.top;
- let colored = if glyph.multicolor { 1 } else { 0 };
+ let colored = if glyph.multicolor {
+ RenderingGlyphFlags::COLORED
+ } else {
+ RenderingGlyphFlags::empty()
+ };
+
let is_wide = if cell.flags.contains(Flags::WIDE_CHAR) { 2 } else { 1 };
let mut vertex = TextVertex {
@@ -322,6 +338,7 @@ pub struct RenderApi<'a> {
atlas: &'a mut Vec<Atlas>,
current_atlas: &'a mut usize,
program: &'a mut TextShaderProgram,
+ dual_source_blending: bool,
}
impl<'a> Drop for RenderApi<'a> {
@@ -375,19 +392,25 @@ impl<'a> TextRenderApi<Batch> for RenderApi<'a> {
gl::BlendFunc(gl::ONE, gl::ZERO);
gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
- // First text rendering pass.
self.program.set_rendering_pass(RenderingPass::SubpixelPass1);
- gl::BlendFuncSeparate(gl::ZERO, gl::ONE_MINUS_SRC_COLOR, gl::ZERO, gl::ONE);
- gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
-
- // Second text rendering pass.
- self.program.set_rendering_pass(RenderingPass::SubpixelPass2);
- gl::BlendFuncSeparate(gl::ONE_MINUS_DST_ALPHA, gl::ONE, gl::ZERO, gl::ONE);
- gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
+ if self.dual_source_blending {
+ // Text rendering pass.
+ gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
+ } else {
+ // First text rendering pass.
+ gl::BlendFuncSeparate(gl::ZERO, gl::ONE_MINUS_SRC_COLOR, gl::ZERO, gl::ONE);
+ gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
+
+ // Second text rendering pass.
+ self.program.set_rendering_pass(RenderingPass::SubpixelPass2);
+ gl::BlendFuncSeparate(gl::ONE_MINUS_DST_ALPHA, gl::ONE, gl::ZERO, gl::ONE);
+ gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
+
+ // Third text rendering pass.
+ self.program.set_rendering_pass(RenderingPass::SubpixelPass3);
+ gl::BlendFuncSeparate(gl::ONE, gl::ONE, gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
+ }
- // Third pass.
- self.program.set_rendering_pass(RenderingPass::SubpixelPass3);
- gl::BlendFuncSeparate(gl::ONE, gl::ONE, gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
}
@@ -416,7 +439,7 @@ struct TextVertex {
b: u8,
// Whether the glyph is colored.
- colored: u8,
+ colored: RenderingGlyphFlags,
// Background color.
bg_r: u8,
@@ -425,15 +448,6 @@ struct TextVertex {
bg_a: u8,
}
-// NOTE: These flags must be in sync with their usage in the gles2/text.*.glsl shaders.
-#[repr(u8)]
-enum RenderingPass {
- Background = 0,
- SubpixelPass1 = 1,
- SubpixelPass2 = 2,
- SubpixelPass3 = 3,
-}
-
#[derive(Debug)]
pub struct TextShaderProgram {
/// Shader program.
@@ -444,8 +458,11 @@ pub struct TextShaderProgram {
/// Rendering pass.
///
- /// The rendering is split into 4 passes. One is used for the background and the rest to
- /// perform subpixel text rendering according to
+ /// For dual source blending, there are 2 passes; one for background, another for text,
+ /// similar to the GLSL3 renderer.
+ ///
+ /// If GL_EXT_blend_func_extended is not available, the rendering is split into 4 passes.
+ /// One is used for the background and the rest to perform subpixel text rendering according to
/// https://github.com/servo/webrender/blob/master/webrender/doc/text-rendering.md.
///
/// Rendering is split into three passes.
@@ -453,8 +470,12 @@ pub struct TextShaderProgram {
}
impl TextShaderProgram {
- pub fn new(shader_version: ShaderVersion) -> Result<Self, Error> {
- let program = ShaderProgram::new(shader_version, TEXT_SHADER_V, TEXT_SHADER_F)?;
+ pub fn new(shader_version: ShaderVersion, dual_source_blending: bool) -> Result<Self, Error> {
+ let fragment_shader =
+ if dual_source_blending { &glsl3::TEXT_SHADER_F } else { &TEXT_SHADER_F };
+
+ let program = ShaderProgram::new(shader_version, TEXT_SHADER_V, fragment_shader)?;
+
Ok(Self {
u_projection: program.get_uniform_location(cstr!("projection"))?,
u_rendering_pass: program.get_uniform_location(cstr!("renderingPass"))?,
diff --git a/alacritty/src/renderer/text/glsl3.rs b/alacritty/src/renderer/text/glsl3.rs
index 917a5fb5..d5413d32 100644
--- a/alacritty/src/renderer/text/glsl3.rs
+++ b/alacritty/src/renderer/text/glsl3.rs
@@ -15,12 +15,12 @@ use crate::renderer::{cstr, Error};
use super::atlas::{Atlas, ATLAS_SIZE};
use super::{
- Glyph, LoadGlyph, LoaderApi, RenderingGlyphFlags, TextRenderApi, TextRenderBatch, TextRenderer,
- TextShader,
+ Glyph, LoadGlyph, LoaderApi, RenderingGlyphFlags, RenderingPass, TextRenderApi,
+ TextRenderBatch, TextRenderer, TextShader,
};
// Shader source.
-static TEXT_SHADER_F: &str = include_str!("../../../res/glsl3/text.f.glsl");
+pub static TEXT_SHADER_F: &str = include_str!("../../../res/glsl3/text.f.glsl");
static TEXT_SHADER_V: &str = include_str!("../../../res/glsl3/text.v.glsl");
/// Maximum items to be drawn in a batch.
@@ -240,7 +240,7 @@ impl<'a> TextRenderApi<Batch> for RenderApi<'a> {
}
unsafe {
- self.program.set_background_pass(true);
+ self.program.set_rendering_pass(RenderingPass::Background);
gl::DrawElementsInstanced(
gl::TRIANGLES,
6,
@@ -248,7 +248,7 @@ impl<'a> TextRenderApi<Batch> for RenderApi<'a> {
ptr::null(),
self.batch.len() as GLsizei,
);
- self.program.set_background_pass(false);
+ self.program.set_rendering_pass(RenderingPass::SubpixelPass1);
gl::DrawElementsInstanced(
gl::TRIANGLES,
6,
@@ -419,8 +419,8 @@ pub struct TextShaderProgram {
/// Background pass flag.
///
- /// Rendering is split into two passes; 1 for backgrounds, and one for text.
- u_background: GLint,
+ /// Rendering is split into two passes; one for backgrounds, and one for text.
+ u_rendering_pass: GLint,
}
impl TextShaderProgram {
@@ -429,7 +429,7 @@ impl TextShaderProgram {
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"))?,
+ u_rendering_pass: program.get_uniform_location(cstr!("renderingPass"))?,
program,
})
}
@@ -440,11 +440,14 @@ impl TextShaderProgram {
}
}
- fn set_background_pass(&self, background_pass: bool) {
- let value = if background_pass { 1 } else { 0 };
+ fn set_rendering_pass(&self, rendering_pass: RenderingPass) {
+ let value = match rendering_pass {
+ RenderingPass::Background | RenderingPass::SubpixelPass1 => rendering_pass as i32,
+ _ => unreachable!("provided pass is not supported in GLSL3 renderer"),
+ };
unsafe {
- gl::Uniform1i(self.u_background, value);
+ gl::Uniform1i(self.u_rendering_pass, value);
}
}
}
diff --git a/alacritty/src/renderer/text/mod.rs b/alacritty/src/renderer/text/mod.rs
index a032ffc7..96945f67 100644
--- a/alacritty/src/renderer/text/mod.rs
+++ b/alacritty/src/renderer/text/mod.rs
@@ -24,11 +24,27 @@ use glyph_cache::{Glyph, LoadGlyph};
bitflags! {
#[repr(C)]
struct RenderingGlyphFlags: u8 {
- const WIDE_CHAR = 0b0000_0001;
- const COLORED = 0b0000_0010;
+ const COLORED = 0b0000_0001;
+ const WIDE_CHAR = 0b0000_0010;
}
}
+/// Rendering passes, for both GLES2 and GLSL3 renderer.
+#[repr(u8)]
+enum RenderingPass {
+ /// Rendering pass used to render background color in text shaders.
+ Background = 0,
+
+ /// The first pass to render text with both GLES2 and GLSL3 renderers.
+ SubpixelPass1 = 1,
+
+ /// The second pass to render text with GLES2 renderer.
+ SubpixelPass2 = 2,
+
+ /// The third pass to render text with GLES2 renderer.
+ SubpixelPass3 = 3,
+}
+
pub trait TextRenderer<'a> {
type Shader: TextShader;
type RenderBatch: TextRenderBatch;