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>
This commit is contained in:
parent
29b1ff59e2
commit
6dc670cde0
|
@ -29,6 +29,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"notify",
|
"notify",
|
||||||
"objc",
|
"objc",
|
||||||
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"png",
|
"png",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
|
@ -1191,9 +1192,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.9.0"
|
version = "1.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
|
|
|
@ -34,6 +34,7 @@ libc = "0.2"
|
||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
dirs = "3.0.1"
|
dirs = "3.0.1"
|
||||||
|
once_cell = "1.12"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gl_generator = "0.14.0"
|
gl_generator = "0.14.0"
|
||||||
|
|
|
@ -1,40 +1,73 @@
|
||||||
|
#if defined(GLES2_RENDERER)
|
||||||
|
// Require extension for dual source blending to work on GLES2.
|
||||||
|
#extension GL_EXT_blend_func_extended: require
|
||||||
|
|
||||||
|
#define int_t highp int
|
||||||
|
#define float_t highp float
|
||||||
|
#define vec3_t mediump vec3
|
||||||
|
#define texture texture2D
|
||||||
|
|
||||||
|
varying mediump vec2 TexCoords;
|
||||||
|
varying mediump vec3 fg;
|
||||||
|
varying highp float colored;
|
||||||
|
varying mediump vec4 bg;
|
||||||
|
|
||||||
|
#define FRAG_COLOR gl_FragColor
|
||||||
|
#define ALPHA_MASK gl_SecondaryFragColorEXT
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define int_t int
|
||||||
|
#define float_t float
|
||||||
|
#define vec3_t vec3
|
||||||
|
|
||||||
in vec2 TexCoords;
|
in vec2 TexCoords;
|
||||||
flat in vec4 fg;
|
flat in vec4 fg;
|
||||||
flat in vec4 bg;
|
flat in vec4 bg;
|
||||||
uniform int backgroundPass;
|
|
||||||
|
|
||||||
layout(location = 0, index = 0) out vec4 color;
|
layout(location = 0, index = 0) out vec4 color;
|
||||||
layout(location = 0, index = 1) out vec4 alphaMask;
|
layout(location = 0, index = 1) out vec4 alphaMask;
|
||||||
|
|
||||||
|
#define FRAG_COLOR color
|
||||||
|
#define ALPHA_MASK alphaMask
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define COLORED 1
|
||||||
|
|
||||||
|
uniform int_t renderingPass;
|
||||||
uniform sampler2D mask;
|
uniform sampler2D mask;
|
||||||
|
|
||||||
#define COLORED 2
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
if (backgroundPass != 0) {
|
if (renderingPass == 0) {
|
||||||
if (bg.a == 0.0) {
|
if (bg.a == 0.0) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
|
|
||||||
alphaMask = vec4(1.0);
|
ALPHA_MASK = vec4(1.0);
|
||||||
|
|
||||||
// Premultiply background color by alpha.
|
// Premultiply background color by alpha.
|
||||||
color = vec4(bg.rgb * bg.a, bg.a);
|
FRAG_COLOR = vec4(bg.rgb * bg.a, bg.a);
|
||||||
} else if ((int(fg.a) & COLORED) != 0) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(GLES2_RENDERER)
|
||||||
|
float_t colored = fg.a;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The wide char information is already stripped, so it's safe to check for equality here.
|
||||||
|
if (int(colored) == COLORED) {
|
||||||
// Color glyphs, like emojis.
|
// Color glyphs, like emojis.
|
||||||
vec4 glyphColor = texture(mask, TexCoords);
|
FRAG_COLOR = texture(mask, TexCoords);
|
||||||
alphaMask = vec4(glyphColor.a);
|
ALPHA_MASK = vec4(FRAG_COLOR.a);
|
||||||
|
|
||||||
// Revert alpha premultiplication.
|
// Revert alpha premultiplication.
|
||||||
if (glyphColor.a != 0) {
|
if (FRAG_COLOR.a != 0.0) {
|
||||||
glyphColor.rgb = vec3(glyphColor.rgb / glyphColor.a);
|
FRAG_COLOR.rgb = vec3(FRAG_COLOR.rgb / FRAG_COLOR.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
color = vec4(glyphColor.rgb, 1.0);
|
FRAG_COLOR = vec4(FRAG_COLOR.rgb, 1.0);
|
||||||
} else {
|
} else {
|
||||||
// Regular text glyphs.
|
// Regular text glyphs.
|
||||||
vec3 textColor = texture(mask, TexCoords).rgb;
|
vec3_t textColor = texture(mask, TexCoords).rgb;
|
||||||
alphaMask = vec4(textColor, textColor.r);
|
ALPHA_MASK = vec4(textColor, textColor.r);
|
||||||
color = vec4(fg.rgb, 1.0);
|
FRAG_COLOR = vec4(fg.rgb, 1.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,9 @@ flat out vec4 bg;
|
||||||
uniform vec2 cellDim;
|
uniform vec2 cellDim;
|
||||||
uniform vec4 projection;
|
uniform vec4 projection;
|
||||||
|
|
||||||
uniform int backgroundPass;
|
uniform int renderingPass;
|
||||||
|
|
||||||
#define WIDE_CHAR 1
|
#define WIDE_CHAR 2
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 projectionOffset = projection.xy;
|
vec2 projectionOffset = projection.xy;
|
||||||
|
@ -39,12 +39,23 @@ void main() {
|
||||||
// Position of cell from top-left
|
// Position of cell from top-left
|
||||||
vec2 cellPosition = cellDim * gridCoords;
|
vec2 cellPosition = cellDim * gridCoords;
|
||||||
|
|
||||||
if (backgroundPass != 0) {
|
fg = vec4(textColor.rgb / 255.0, textColor.a);
|
||||||
|
bg = backgroundColor / 255.0;
|
||||||
|
|
||||||
|
float occupiedCells = 1;
|
||||||
|
if ((int(fg.a) >= WIDE_CHAR)) {
|
||||||
|
// Update wide char x dimension so it'll cover the following spacer.
|
||||||
|
occupiedCells = 2;
|
||||||
|
|
||||||
|
// Since we don't perform bitwise operations due to limitations of
|
||||||
|
// the GLES2 renderer,we subtract wide char bits keeping only colored.
|
||||||
|
fg.a = round(fg.a - WIDE_CHAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderingPass == 0) {
|
||||||
vec2 backgroundDim = cellDim;
|
vec2 backgroundDim = cellDim;
|
||||||
if ((int(textColor.a) & WIDE_CHAR) != 0) {
|
backgroundDim.x *= occupiedCells;
|
||||||
// Update wide char x dimension so it'll cover the following spacer.
|
|
||||||
backgroundDim.x *= 2;
|
|
||||||
}
|
|
||||||
vec2 finalPosition = cellPosition + backgroundDim * position;
|
vec2 finalPosition = cellPosition + backgroundDim * position;
|
||||||
gl_Position =
|
gl_Position =
|
||||||
vec4(projectionOffset + projectionScale * finalPosition, 0.0, 1.0);
|
vec4(projectionOffset + projectionScale * finalPosition, 0.0, 1.0);
|
||||||
|
@ -63,7 +74,4 @@ void main() {
|
||||||
vec2 uvSize = uv.zw;
|
vec2 uvSize = uv.zw;
|
||||||
TexCoords = uvOffset + position * uvSize;
|
TexCoords = uvOffset + position * uvSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
bg = backgroundColor / 255.0;
|
|
||||||
fg = vec4(textColor.rgb / 255.0, textColor.a);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ varying color_t color;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define float_t float
|
#define float_t float
|
||||||
#define int_t int
|
|
||||||
#define color_t vec4
|
#define color_t vec4
|
||||||
|
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crossfont::Metrics;
|
use crossfont::Metrics;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
use alacritty_terminal::index::Point;
|
use alacritty_terminal::index::Point;
|
||||||
use alacritty_terminal::term::cell::Flags;
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,11 +11,12 @@ use crate::display::SizeInfo;
|
||||||
use crate::gl;
|
use crate::gl;
|
||||||
use crate::gl::types::*;
|
use crate::gl::types::*;
|
||||||
use crate::renderer::shader::{ShaderProgram, ShaderVersion};
|
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::atlas::{Atlas, ATLAS_SIZE};
|
||||||
use super::{
|
use super::{
|
||||||
Glyph, LoadGlyph, LoaderApi, TextRenderApi, TextRenderBatch, TextRenderer, TextShader,
|
glsl3, Glyph, LoadGlyph, LoaderApi, RenderingGlyphFlags, RenderingPass, TextRenderApi,
|
||||||
|
TextRenderBatch, TextRenderer, TextShader,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Shader source.
|
// Shader source.
|
||||||
|
@ -32,13 +33,21 @@ pub struct Gles2Renderer {
|
||||||
batch: Batch,
|
batch: Batch,
|
||||||
current_atlas: usize,
|
current_atlas: usize,
|
||||||
active_tex: GLuint,
|
active_tex: GLuint,
|
||||||
|
dual_source_blending: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gles2Renderer {
|
impl Gles2Renderer {
|
||||||
pub fn new() -> Result<Self, Error> {
|
pub fn new() -> Result<Self, Error> {
|
||||||
info!("Using OpenGL ES 2.0 renderer");
|
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 vao: GLuint = 0;
|
||||||
let mut vbo: GLuint = 0;
|
let mut vbo: GLuint = 0;
|
||||||
let mut ebo: GLuint = 0;
|
let mut ebo: GLuint = 0;
|
||||||
|
@ -139,6 +148,7 @@ impl Gles2Renderer {
|
||||||
batch: Batch::new(),
|
batch: Batch::new(),
|
||||||
current_atlas: 0,
|
current_atlas: 0,
|
||||||
active_tex: 0,
|
active_tex: 0,
|
||||||
|
dual_source_blending,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,6 +190,7 @@ impl<'a> TextRenderer<'a> for Gles2Renderer {
|
||||||
atlas: &mut self.atlas,
|
atlas: &mut self.atlas,
|
||||||
current_atlas: &mut self.current_atlas,
|
current_atlas: &mut self.current_atlas,
|
||||||
program: &mut self.program,
|
program: &mut self.program,
|
||||||
|
dual_source_blending: self.dual_source_blending,
|
||||||
});
|
});
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -205,7 +216,7 @@ impl<'a> TextRenderer<'a> for Gles2Renderer {
|
||||||
/// Maximum items to be drawn in a batch.
|
/// 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),
|
/// 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;
|
const BATCH_MAX: usize = (u16::MAX - u16::MAX % 4) as usize;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[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_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 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 is_wide = if cell.flags.contains(Flags::WIDE_CHAR) { 2 } else { 1 };
|
||||||
|
|
||||||
let mut vertex = TextVertex {
|
let mut vertex = TextVertex {
|
||||||
|
@ -322,6 +338,7 @@ pub struct RenderApi<'a> {
|
||||||
atlas: &'a mut Vec<Atlas>,
|
atlas: &'a mut Vec<Atlas>,
|
||||||
current_atlas: &'a mut usize,
|
current_atlas: &'a mut usize,
|
||||||
program: &'a mut TextShaderProgram,
|
program: &'a mut TextShaderProgram,
|
||||||
|
dual_source_blending: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Drop for RenderApi<'a> {
|
impl<'a> Drop for RenderApi<'a> {
|
||||||
|
@ -375,19 +392,25 @@ impl<'a> TextRenderApi<Batch> for RenderApi<'a> {
|
||||||
gl::BlendFunc(gl::ONE, gl::ZERO);
|
gl::BlendFunc(gl::ONE, gl::ZERO);
|
||||||
gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
|
gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
|
||||||
|
|
||||||
// First text rendering pass.
|
|
||||||
self.program.set_rendering_pass(RenderingPass::SubpixelPass1);
|
self.program.set_rendering_pass(RenderingPass::SubpixelPass1);
|
||||||
gl::BlendFuncSeparate(gl::ZERO, gl::ONE_MINUS_SRC_COLOR, gl::ZERO, gl::ONE);
|
if self.dual_source_blending {
|
||||||
gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
|
// 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.
|
// Second text rendering pass.
|
||||||
self.program.set_rendering_pass(RenderingPass::SubpixelPass2);
|
self.program.set_rendering_pass(RenderingPass::SubpixelPass2);
|
||||||
gl::BlendFuncSeparate(gl::ONE_MINUS_DST_ALPHA, gl::ONE, gl::ZERO, gl::ONE);
|
gl::BlendFuncSeparate(gl::ONE_MINUS_DST_ALPHA, gl::ONE, gl::ZERO, gl::ONE);
|
||||||
gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
|
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());
|
gl::DrawElements(gl::TRIANGLES, num_indices, gl::UNSIGNED_SHORT, ptr::null());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,7 +439,7 @@ struct TextVertex {
|
||||||
b: u8,
|
b: u8,
|
||||||
|
|
||||||
// Whether the glyph is colored.
|
// Whether the glyph is colored.
|
||||||
colored: u8,
|
colored: RenderingGlyphFlags,
|
||||||
|
|
||||||
// Background color.
|
// Background color.
|
||||||
bg_r: u8,
|
bg_r: u8,
|
||||||
|
@ -425,15 +448,6 @@ struct TextVertex {
|
||||||
bg_a: u8,
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct TextShaderProgram {
|
pub struct TextShaderProgram {
|
||||||
/// Shader program.
|
/// Shader program.
|
||||||
|
@ -444,8 +458,11 @@ pub struct TextShaderProgram {
|
||||||
|
|
||||||
/// Rendering pass.
|
/// Rendering pass.
|
||||||
///
|
///
|
||||||
/// The rendering is split into 4 passes. One is used for the background and the rest to
|
/// For dual source blending, there are 2 passes; one for background, another for text,
|
||||||
/// perform subpixel text rendering according to
|
/// 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.
|
/// https://github.com/servo/webrender/blob/master/webrender/doc/text-rendering.md.
|
||||||
///
|
///
|
||||||
/// Rendering is split into three passes.
|
/// Rendering is split into three passes.
|
||||||
|
@ -453,8 +470,12 @@ pub struct TextShaderProgram {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextShaderProgram {
|
impl TextShaderProgram {
|
||||||
pub fn new(shader_version: ShaderVersion) -> Result<Self, Error> {
|
pub fn new(shader_version: ShaderVersion, dual_source_blending: bool) -> Result<Self, Error> {
|
||||||
let program = ShaderProgram::new(shader_version, TEXT_SHADER_V, TEXT_SHADER_F)?;
|
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 {
|
Ok(Self {
|
||||||
u_projection: program.get_uniform_location(cstr!("projection"))?,
|
u_projection: program.get_uniform_location(cstr!("projection"))?,
|
||||||
u_rendering_pass: program.get_uniform_location(cstr!("renderingPass"))?,
|
u_rendering_pass: program.get_uniform_location(cstr!("renderingPass"))?,
|
||||||
|
|
|
@ -15,12 +15,12 @@ use crate::renderer::{cstr, Error};
|
||||||
|
|
||||||
use super::atlas::{Atlas, ATLAS_SIZE};
|
use super::atlas::{Atlas, ATLAS_SIZE};
|
||||||
use super::{
|
use super::{
|
||||||
Glyph, LoadGlyph, LoaderApi, RenderingGlyphFlags, TextRenderApi, TextRenderBatch, TextRenderer,
|
Glyph, LoadGlyph, LoaderApi, RenderingGlyphFlags, RenderingPass, TextRenderApi,
|
||||||
TextShader,
|
TextRenderBatch, TextRenderer, TextShader,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Shader source.
|
// 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");
|
static TEXT_SHADER_V: &str = include_str!("../../../res/glsl3/text.v.glsl");
|
||||||
|
|
||||||
/// Maximum items to be drawn in a batch.
|
/// Maximum items to be drawn in a batch.
|
||||||
|
@ -240,7 +240,7 @@ impl<'a> TextRenderApi<Batch> for RenderApi<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
self.program.set_background_pass(true);
|
self.program.set_rendering_pass(RenderingPass::Background);
|
||||||
gl::DrawElementsInstanced(
|
gl::DrawElementsInstanced(
|
||||||
gl::TRIANGLES,
|
gl::TRIANGLES,
|
||||||
6,
|
6,
|
||||||
|
@ -248,7 +248,7 @@ impl<'a> TextRenderApi<Batch> for RenderApi<'a> {
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
self.batch.len() as GLsizei,
|
self.batch.len() as GLsizei,
|
||||||
);
|
);
|
||||||
self.program.set_background_pass(false);
|
self.program.set_rendering_pass(RenderingPass::SubpixelPass1);
|
||||||
gl::DrawElementsInstanced(
|
gl::DrawElementsInstanced(
|
||||||
gl::TRIANGLES,
|
gl::TRIANGLES,
|
||||||
6,
|
6,
|
||||||
|
@ -419,8 +419,8 @@ pub struct TextShaderProgram {
|
||||||
|
|
||||||
/// Background pass flag.
|
/// Background pass flag.
|
||||||
///
|
///
|
||||||
/// Rendering is split into two passes; 1 for backgrounds, and one for text.
|
/// Rendering is split into two passes; one for backgrounds, and one for text.
|
||||||
u_background: GLint,
|
u_rendering_pass: GLint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextShaderProgram {
|
impl TextShaderProgram {
|
||||||
|
@ -429,7 +429,7 @@ impl TextShaderProgram {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
u_projection: program.get_uniform_location(cstr!("projection"))?,
|
u_projection: program.get_uniform_location(cstr!("projection"))?,
|
||||||
u_cell_dim: program.get_uniform_location(cstr!("cellDim"))?,
|
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,
|
program,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -440,11 +440,14 @@ impl TextShaderProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_background_pass(&self, background_pass: bool) {
|
fn set_rendering_pass(&self, rendering_pass: RenderingPass) {
|
||||||
let value = if background_pass { 1 } else { 0 };
|
let value = match rendering_pass {
|
||||||
|
RenderingPass::Background | RenderingPass::SubpixelPass1 => rendering_pass as i32,
|
||||||
|
_ => unreachable!("provided pass is not supported in GLSL3 renderer"),
|
||||||
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::Uniform1i(self.u_background, value);
|
gl::Uniform1i(self.u_rendering_pass, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,27 @@ use glyph_cache::{Glyph, LoadGlyph};
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct RenderingGlyphFlags: u8 {
|
struct RenderingGlyphFlags: u8 {
|
||||||
const WIDE_CHAR = 0b0000_0001;
|
const COLORED = 0b0000_0001;
|
||||||
const COLORED = 0b0000_0010;
|
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> {
|
pub trait TextRenderer<'a> {
|
||||||
type Shader: TextShader;
|
type Shader: TextShader;
|
||||||
type RenderBatch: TextRenderBatch;
|
type RenderBatch: TextRenderBatch;
|
||||||
|
|
Loading…
Reference in New Issue