2020-10-19 19:22:02 +03:00
|
|
|
use super::glyphcache::GlyphCache;
|
2019-12-24 06:13:15 +03:00
|
|
|
use super::quad::*;
|
2019-10-27 02:59:53 +03:00
|
|
|
use super::utilsprites::{RenderMetrics, UtilSprites};
|
2020-10-26 02:09:47 +03:00
|
|
|
use ::window::bitmaps::atlas::OutOfTextureSpace;
|
2019-10-27 02:59:53 +03:00
|
|
|
use ::window::glium::backend::Context as GliumContext;
|
2021-07-30 22:59:15 +03:00
|
|
|
use ::window::glium::buffer::Mapping;
|
2019-10-27 02:59:53 +03:00
|
|
|
use ::window::glium::texture::SrgbTexture2d;
|
|
|
|
use ::window::glium::{IndexBuffer, VertexBuffer};
|
|
|
|
use ::window::*;
|
2021-07-30 08:21:46 +03:00
|
|
|
use std::cell::{Ref, RefCell, RefMut};
|
2019-10-27 02:59:53 +03:00
|
|
|
use std::rc::Rc;
|
2020-10-25 02:40:15 +03:00
|
|
|
use wezterm_font::FontConfiguration;
|
2019-10-27 02:59:53 +03:00
|
|
|
|
2021-07-30 22:59:15 +03:00
|
|
|
const INDICES_PER_CELL: usize = 6;
|
|
|
|
|
|
|
|
pub struct MappedQuads<'a> {
|
|
|
|
mapping: Mapping<'a, [Vertex]>,
|
|
|
|
next: RefMut<'a, usize>,
|
|
|
|
capacity: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> MappedQuads<'a> {
|
|
|
|
pub fn allocate<'b>(&'b mut self) -> anyhow::Result<Quad<'b>> {
|
|
|
|
let idx = *self.next;
|
|
|
|
*self.next += 1;
|
2021-07-31 02:59:47 +03:00
|
|
|
let idx = if idx >= self.capacity {
|
|
|
|
// We don't have enough quads, so we'll keep re-using
|
|
|
|
// the first quad until we reach the end of the render
|
|
|
|
// pass, at which point we'll detect this condition
|
|
|
|
// and re-allocate the quads.
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
idx
|
|
|
|
};
|
2021-07-30 22:59:15 +03:00
|
|
|
|
|
|
|
let idx = idx * VERTICES_PER_CELL;
|
|
|
|
let mut quad = Quad {
|
|
|
|
vert: &mut self.mapping[idx..idx + VERTICES_PER_CELL],
|
|
|
|
};
|
|
|
|
|
|
|
|
quad.set_texture_adjust(0., 0., 0., 0.);
|
|
|
|
quad.set_has_color(false);
|
|
|
|
|
|
|
|
Ok(quad)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-21 22:01:30 +03:00
|
|
|
pub struct TripleVertexBuffer {
|
2021-07-30 08:21:46 +03:00
|
|
|
pub index: RefCell<usize>,
|
|
|
|
pub bufs: RefCell<[VertexBuffer<Vertex>; 3]>,
|
|
|
|
pub indices: IndexBuffer<u32>,
|
|
|
|
pub capacity: usize,
|
2021-07-30 22:59:15 +03:00
|
|
|
pub next_quad: RefCell<usize>,
|
2021-07-30 08:21:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl TripleVertexBuffer {
|
2021-07-30 22:59:15 +03:00
|
|
|
pub fn clear_quad_allocation(&self) {
|
|
|
|
*self.next_quad.borrow_mut() = 0;
|
|
|
|
}
|
|
|
|
|
2021-07-31 02:59:47 +03:00
|
|
|
pub fn need_more_quads(&self) -> Option<usize> {
|
|
|
|
let next = *self.next_quad.borrow();
|
|
|
|
if next > self.capacity {
|
|
|
|
Some(next)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-30 22:59:15 +03:00
|
|
|
pub fn vertex_index_count(&self) -> (usize, usize) {
|
|
|
|
let num_quads = *self.next_quad.borrow();
|
|
|
|
(num_quads * VERTICES_PER_CELL, num_quads * INDICES_PER_CELL)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn map<'a>(&'a self, bufs: &'a mut RefMut<VertexBuffer<Vertex>>) -> MappedQuads<'a> {
|
|
|
|
let mapping = bufs.slice_mut(..).expect("to map vertex buffer").map();
|
|
|
|
MappedQuads {
|
|
|
|
mapping,
|
|
|
|
next: self.next_quad.borrow_mut(),
|
|
|
|
capacity: self.capacity,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-30 08:21:46 +03:00
|
|
|
pub fn current_vb(&self) -> Ref<VertexBuffer<Vertex>> {
|
|
|
|
let index = *self.index.borrow();
|
|
|
|
let bufs = self.bufs.borrow();
|
|
|
|
Ref::map(bufs, |bufs| &bufs[index])
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn current_vb_mut(&self) -> RefMut<VertexBuffer<Vertex>> {
|
|
|
|
let index = *self.index.borrow();
|
|
|
|
let bufs = self.bufs.borrow_mut();
|
|
|
|
RefMut::map(bufs, |bufs| &mut bufs[index])
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn next_index(&self) {
|
|
|
|
let mut index = self.index.borrow_mut();
|
|
|
|
*index += 1;
|
|
|
|
if *index >= 3 {
|
|
|
|
*index = 0;
|
|
|
|
}
|
|
|
|
}
|
2021-03-21 22:01:30 +03:00
|
|
|
}
|
|
|
|
|
2021-01-16 19:00:17 +03:00
|
|
|
pub struct RenderState {
|
2019-10-27 02:59:53 +03:00
|
|
|
pub context: Rc<GliumContext>,
|
|
|
|
pub glyph_cache: RefCell<GlyphCache<SrgbTexture2d>>,
|
|
|
|
pub util_sprites: UtilSprites<SrgbTexture2d>,
|
2021-02-21 04:02:21 +03:00
|
|
|
pub glyph_prog: glium::Program,
|
2021-07-30 08:21:46 +03:00
|
|
|
pub glyph_vertex_buffer: TripleVertexBuffer,
|
2019-10-27 02:59:53 +03:00
|
|
|
}
|
|
|
|
|
2021-01-16 19:00:17 +03:00
|
|
|
impl RenderState {
|
2019-10-27 02:59:53 +03:00
|
|
|
pub fn new(
|
|
|
|
context: Rc<GliumContext>,
|
|
|
|
fonts: &Rc<FontConfiguration>,
|
|
|
|
metrics: &RenderMetrics,
|
2020-10-26 02:09:47 +03:00
|
|
|
mut atlas_size: usize,
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<Self> {
|
2020-10-26 02:09:47 +03:00
|
|
|
loop {
|
2021-01-06 01:16:21 +03:00
|
|
|
let glyph_cache =
|
|
|
|
RefCell::new(GlyphCache::new_gl(&context, fonts, atlas_size, metrics)?);
|
2020-10-26 02:09:47 +03:00
|
|
|
let result = UtilSprites::new(&mut *glyph_cache.borrow_mut(), metrics);
|
|
|
|
match result {
|
|
|
|
Ok(util_sprites) => {
|
2021-02-21 04:02:21 +03:00
|
|
|
// Last prog outputs srgb for gamma correction
|
2021-07-31 03:27:24 +03:00
|
|
|
let glyph_prog = Self::compile_prog(&context, true, Self::glyph_shader)?;
|
2020-10-26 02:09:47 +03:00
|
|
|
|
2021-07-31 02:59:47 +03:00
|
|
|
let glyph_vertex_buffer = Self::compute_vertices(&context, 1024)?;
|
2020-10-26 02:09:47 +03:00
|
|
|
|
|
|
|
return Ok(Self {
|
|
|
|
context,
|
|
|
|
glyph_cache,
|
|
|
|
util_sprites,
|
2021-02-21 04:02:21 +03:00
|
|
|
glyph_prog,
|
2021-07-30 08:21:46 +03:00
|
|
|
glyph_vertex_buffer,
|
2020-10-26 02:09:47 +03:00
|
|
|
});
|
|
|
|
}
|
2021-03-14 10:18:15 +03:00
|
|
|
Err(OutOfTextureSpace {
|
|
|
|
size: Some(size), ..
|
|
|
|
}) => {
|
2020-10-26 02:09:47 +03:00
|
|
|
atlas_size = size;
|
|
|
|
}
|
2021-03-14 10:18:15 +03:00
|
|
|
Err(OutOfTextureSpace { size: None, .. }) => {
|
2020-10-26 02:09:47 +03:00
|
|
|
anyhow::bail!("requested texture size is impossible!?")
|
2019-10-27 02:59:53 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-21 04:02:21 +03:00
|
|
|
fn compile_prog(
|
|
|
|
context: &Rc<GliumContext>,
|
|
|
|
outputs_srgb: bool,
|
|
|
|
fragment_shader: fn(&str) -> (String, String),
|
|
|
|
) -> anyhow::Result<glium::Program> {
|
|
|
|
let mut errors = vec![];
|
|
|
|
for version in &["330", "300 es"] {
|
|
|
|
let (vertex_shader, fragment_shader) = fragment_shader(version);
|
|
|
|
let source = glium::program::ProgramCreationInput::SourceCode {
|
|
|
|
vertex_shader: &vertex_shader,
|
|
|
|
fragment_shader: &fragment_shader,
|
|
|
|
outputs_srgb,
|
|
|
|
tessellation_control_shader: None,
|
|
|
|
tessellation_evaluation_shader: None,
|
|
|
|
transform_feedback_varyings: None,
|
|
|
|
uses_point_size: false,
|
|
|
|
geometry_shader: None,
|
|
|
|
};
|
|
|
|
log::trace!("compiling a prog with version {}", version);
|
|
|
|
match glium::Program::new(context, source) {
|
|
|
|
Ok(prog) => {
|
|
|
|
return Ok(prog);
|
|
|
|
}
|
|
|
|
Err(err) => errors.push(err.to_string()),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
anyhow::bail!("Failed to compile shaders: {}", errors.join("\n"))
|
|
|
|
}
|
|
|
|
|
2021-07-31 02:59:47 +03:00
|
|
|
pub fn reallocate_quads(&mut self, num_quads: usize) -> anyhow::Result<()> {
|
|
|
|
let glyph_vertex_buffer = Self::compute_vertices(&self.context, num_quads)?;
|
2019-10-27 02:59:53 +03:00
|
|
|
|
2021-07-30 08:21:46 +03:00
|
|
|
self.glyph_vertex_buffer = glyph_vertex_buffer;
|
2019-10-27 02:59:53 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-02-21 04:02:21 +03:00
|
|
|
fn glyph_shader(version: &str) -> (String, String) {
|
|
|
|
(
|
|
|
|
format!(
|
|
|
|
"#version {}\n{}\n{}",
|
|
|
|
version,
|
|
|
|
include_str!("vertex-common.glsl"),
|
|
|
|
include_str!("glyph-vertex.glsl")
|
|
|
|
),
|
|
|
|
format!(
|
|
|
|
"#version {}\n{}\n{}",
|
|
|
|
version,
|
|
|
|
include_str!("fragment-common.glsl"),
|
|
|
|
include_str!("glyph-frag.glsl")
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-10-27 02:59:53 +03:00
|
|
|
/// Compute a vertex buffer to hold the quads that comprise the visible
|
|
|
|
/// portion of the screen. We recreate this when the screen is resized.
|
2019-12-22 01:17:50 +03:00
|
|
|
/// The idea is that we want to minimize any heavy lifting and computation
|
2019-10-27 02:59:53 +03:00
|
|
|
/// and instead just poke some attributes into the offset that corresponds
|
|
|
|
/// to a changed cell when we need to repaint the screen, and then just
|
|
|
|
/// let the GPU figure out the rest.
|
|
|
|
fn compute_vertices(
|
|
|
|
context: &Rc<GliumContext>,
|
2021-07-31 02:59:47 +03:00
|
|
|
num_quads: usize,
|
2021-07-30 08:21:46 +03:00
|
|
|
) -> anyhow::Result<TripleVertexBuffer> {
|
2021-07-30 22:59:15 +03:00
|
|
|
let verts = vec![Vertex::default(); num_quads * VERTICES_PER_CELL];
|
|
|
|
let mut indices = vec![];
|
|
|
|
indices.reserve(num_quads * INDICES_PER_CELL);
|
|
|
|
|
|
|
|
for q in 0..num_quads {
|
|
|
|
let idx = (q * VERTICES_PER_CELL) as u32;
|
2019-12-23 05:57:54 +03:00
|
|
|
|
|
|
|
// Emit two triangles to form the glyph quad
|
|
|
|
indices.push(idx + V_TOP_LEFT as u32);
|
|
|
|
indices.push(idx + V_TOP_RIGHT as u32);
|
|
|
|
indices.push(idx + V_BOT_LEFT as u32);
|
|
|
|
|
|
|
|
indices.push(idx + V_TOP_RIGHT as u32);
|
|
|
|
indices.push(idx + V_BOT_LEFT as u32);
|
|
|
|
indices.push(idx + V_BOT_RIGHT as u32);
|
2019-12-24 06:13:15 +03:00
|
|
|
}
|
|
|
|
|
2021-03-21 22:01:30 +03:00
|
|
|
let buffer = TripleVertexBuffer {
|
2021-07-30 08:21:46 +03:00
|
|
|
index: RefCell::new(0),
|
|
|
|
bufs: RefCell::new([
|
2021-03-22 06:51:02 +03:00
|
|
|
VertexBuffer::dynamic(context, &verts)?,
|
|
|
|
VertexBuffer::dynamic(context, &verts)?,
|
|
|
|
VertexBuffer::dynamic(context, &verts)?,
|
2021-07-30 08:21:46 +03:00
|
|
|
]),
|
2021-07-31 02:59:47 +03:00
|
|
|
capacity: num_quads,
|
2021-07-30 08:21:46 +03:00
|
|
|
indices: IndexBuffer::new(
|
2019-10-27 02:59:53 +03:00
|
|
|
context,
|
|
|
|
glium::index::PrimitiveType::TrianglesList,
|
|
|
|
&indices,
|
|
|
|
)?,
|
2021-07-30 22:59:15 +03:00
|
|
|
next_quad: RefCell::new(0),
|
2021-07-30 08:21:46 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(buffer)
|
2019-10-27 02:59:53 +03:00
|
|
|
}
|
|
|
|
|
2020-10-23 23:57:58 +03:00
|
|
|
pub fn clear_texture_atlas(&mut self, metrics: &RenderMetrics) -> anyhow::Result<()> {
|
2021-01-16 19:00:17 +03:00
|
|
|
let mut glyph_cache = self.glyph_cache.borrow_mut();
|
|
|
|
glyph_cache.clear();
|
|
|
|
self.util_sprites = UtilSprites::new(&mut glyph_cache, metrics)?;
|
2020-10-23 23:57:58 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-10-27 02:59:53 +03:00
|
|
|
pub fn recreate_texture_atlas(
|
|
|
|
&mut self,
|
|
|
|
fonts: &Rc<FontConfiguration>,
|
|
|
|
metrics: &RenderMetrics,
|
|
|
|
size: Option<usize>,
|
2021-01-16 19:12:45 +03:00
|
|
|
) -> anyhow::Result<()> {
|
|
|
|
// We make a a couple of passes at resizing; if the user has selected a large
|
|
|
|
// font size (or a large scaling factor) then the `size==None` case will not
|
|
|
|
// be able to fit the initial utility glyphs and apply_scale_change won't
|
|
|
|
// be able to deal with that error situation. Rather than make every
|
|
|
|
// caller know how to deal with OutOfTextureSpace we try to absorb
|
|
|
|
// and accomodate that here.
|
|
|
|
let mut size = size;
|
|
|
|
let mut attempt = 10;
|
|
|
|
loop {
|
|
|
|
match self.recreate_texture_atlas_impl(fonts, metrics, size) {
|
|
|
|
Ok(_) => return Ok(()),
|
|
|
|
Err(err) => {
|
|
|
|
attempt -= 1;
|
|
|
|
if attempt == 0 {
|
|
|
|
return Err(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(&OutOfTextureSpace {
|
|
|
|
size: Some(needed_size),
|
2021-03-14 10:18:15 +03:00
|
|
|
..
|
2021-01-16 19:12:45 +03:00
|
|
|
}) = err.downcast_ref::<OutOfTextureSpace>()
|
|
|
|
{
|
|
|
|
size.replace(needed_size);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Err(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn recreate_texture_atlas_impl(
|
|
|
|
&mut self,
|
|
|
|
fonts: &Rc<FontConfiguration>,
|
|
|
|
metrics: &RenderMetrics,
|
|
|
|
size: Option<usize>,
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<()> {
|
2021-01-16 19:00:17 +03:00
|
|
|
let size = size.unwrap_or_else(|| self.glyph_cache.borrow().atlas.size());
|
2021-03-14 20:29:02 +03:00
|
|
|
let mut new_glyph_cache = GlyphCache::new_gl(&self.context, fonts, size, metrics)?;
|
|
|
|
self.util_sprites = UtilSprites::new(&mut new_glyph_cache, metrics)?;
|
|
|
|
|
|
|
|
let mut glyph_cache = self.glyph_cache.borrow_mut();
|
|
|
|
|
|
|
|
// Steal the decoded image cache; without this, any animating gifs
|
|
|
|
// would reset back to frame 0 each time we filled the texture
|
|
|
|
std::mem::swap(
|
|
|
|
&mut glyph_cache.image_cache,
|
|
|
|
&mut new_glyph_cache.image_cache,
|
|
|
|
);
|
|
|
|
|
|
|
|
*glyph_cache = new_glyph_cache;
|
2019-10-27 02:59:53 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|