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;
|
|
|
|
use ::window::glium::texture::SrgbTexture2d;
|
|
|
|
use ::window::glium::{IndexBuffer, VertexBuffer};
|
|
|
|
use ::window::*;
|
2020-10-19 19:22:02 +03:00
|
|
|
use anyhow::anyhow;
|
|
|
|
use config::configuration;
|
2019-10-27 02:59:53 +03:00
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::rc::Rc;
|
2020-10-25 02:40:15 +03:00
|
|
|
use wezterm_font::FontConfiguration;
|
2019-10-27 02:59:53 +03:00
|
|
|
|
2020-10-19 19:22:02 +03:00
|
|
|
pub struct SoftwareRenderState {}
|
2019-10-27 02:59:53 +03:00
|
|
|
|
|
|
|
impl SoftwareRenderState {
|
2020-10-19 19:22:02 +03:00
|
|
|
pub fn new() -> anyhow::Result<Self> {
|
|
|
|
Ok(Self {})
|
2019-10-27 02:59:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct OpenGLRenderState {
|
|
|
|
pub context: Rc<GliumContext>,
|
|
|
|
pub glyph_cache: RefCell<GlyphCache<SrgbTexture2d>>,
|
|
|
|
pub util_sprites: UtilSprites<SrgbTexture2d>,
|
|
|
|
pub program: glium::Program,
|
|
|
|
pub glyph_vertex_buffer: RefCell<VertexBuffer<Vertex>>,
|
|
|
|
pub glyph_index_buffer: IndexBuffer<u32>,
|
2019-12-24 06:13:15 +03:00
|
|
|
pub quads: Quads,
|
2019-10-27 02:59:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl OpenGLRenderState {
|
|
|
|
pub fn new(
|
|
|
|
context: Rc<GliumContext>,
|
|
|
|
fonts: &Rc<FontConfiguration>,
|
|
|
|
metrics: &RenderMetrics,
|
2020-10-26 02:09:47 +03:00
|
|
|
mut atlas_size: usize,
|
2019-10-27 02:59:53 +03:00
|
|
|
pixel_width: usize,
|
|
|
|
pixel_height: 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) => {
|
|
|
|
let mut errors = vec![];
|
|
|
|
let mut program = None;
|
|
|
|
for version in &["330", "300 es"] {
|
|
|
|
let source = glium::program::ProgramCreationInput::SourceCode {
|
|
|
|
vertex_shader: &Self::vertex_shader(version),
|
|
|
|
fragment_shader: &Self::fragment_shader(version),
|
|
|
|
outputs_srgb: true,
|
|
|
|
tessellation_control_shader: None,
|
|
|
|
tessellation_evaluation_shader: None,
|
|
|
|
transform_feedback_varyings: None,
|
|
|
|
uses_point_size: false,
|
|
|
|
geometry_shader: None,
|
|
|
|
};
|
2020-12-21 09:01:06 +03:00
|
|
|
log::trace!("compiling a prog with version {}", version);
|
2020-10-26 02:09:47 +03:00
|
|
|
match glium::Program::new(&context, source) {
|
|
|
|
Ok(prog) => {
|
|
|
|
program = Some(prog);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Err(err) => errors.push(err.to_string()),
|
|
|
|
};
|
|
|
|
}
|
2019-10-27 02:59:53 +03:00
|
|
|
|
2020-10-26 02:09:47 +03:00
|
|
|
let program = program.ok_or_else(|| {
|
|
|
|
anyhow!("Failed to compile shaders: {}", errors.join("\n"))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let (glyph_vertex_buffer, glyph_index_buffer, quads) = Self::compute_vertices(
|
|
|
|
&context,
|
|
|
|
metrics,
|
|
|
|
pixel_width as f32,
|
|
|
|
pixel_height as f32,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
return Ok(Self {
|
|
|
|
context,
|
|
|
|
glyph_cache,
|
|
|
|
util_sprites,
|
|
|
|
program,
|
|
|
|
glyph_vertex_buffer: RefCell::new(glyph_vertex_buffer),
|
|
|
|
glyph_index_buffer,
|
|
|
|
quads,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
Err(OutOfTextureSpace { size: Some(size) }) => {
|
|
|
|
atlas_size = size;
|
|
|
|
}
|
|
|
|
Err(OutOfTextureSpace { size: None }) => {
|
|
|
|
anyhow::bail!("requested texture size is impossible!?")
|
2019-10-27 02:59:53 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn advise_of_window_size_change(
|
|
|
|
&mut self,
|
|
|
|
metrics: &RenderMetrics,
|
|
|
|
pixel_width: usize,
|
|
|
|
pixel_height: usize,
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<()> {
|
2019-12-24 06:13:15 +03:00
|
|
|
let (glyph_vertex_buffer, glyph_index_buffer, quads) = Self::compute_vertices(
|
2019-10-27 02:59:53 +03:00
|
|
|
&self.context,
|
|
|
|
metrics,
|
|
|
|
pixel_width as f32,
|
|
|
|
pixel_height as f32,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
*self.glyph_vertex_buffer.borrow_mut() = glyph_vertex_buffer;
|
|
|
|
self.glyph_index_buffer = glyph_index_buffer;
|
2019-12-24 06:13:15 +03:00
|
|
|
self.quads = quads;
|
2019-10-27 02:59:53 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn vertex_shader(version: &str) -> String {
|
|
|
|
format!("#version {}\n{}", version, include_str!("vertex.glsl"))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fragment_shader(version: &str) -> String {
|
|
|
|
format!("#version {}\n{}", version, include_str!("fragment.glsl"))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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>,
|
|
|
|
metrics: &RenderMetrics,
|
|
|
|
width: f32,
|
|
|
|
height: f32,
|
2019-12-24 06:13:15 +03:00
|
|
|
) -> anyhow::Result<(VertexBuffer<Vertex>, IndexBuffer<u32>, Quads)> {
|
2019-10-27 02:59:53 +03:00
|
|
|
let cell_width = metrics.cell_size.width as f32;
|
|
|
|
let cell_height = metrics.cell_size.height as f32;
|
|
|
|
let mut verts = Vec::new();
|
|
|
|
let mut indices = Vec::new();
|
|
|
|
|
2019-12-22 01:17:50 +03:00
|
|
|
let config = configuration();
|
2019-12-23 05:57:54 +03:00
|
|
|
let padding_right = super::termwindow::effective_right_padding(&config, metrics);
|
|
|
|
let avail_width =
|
|
|
|
(width as usize).saturating_sub((config.window_padding.left + padding_right) as usize);
|
2019-12-22 01:17:50 +03:00
|
|
|
let avail_height = (height as usize)
|
|
|
|
.saturating_sub((config.window_padding.top + config.window_padding.bottom) as usize);
|
|
|
|
|
|
|
|
let num_cols = avail_width as usize / cell_width as usize;
|
|
|
|
let num_rows = avail_height as usize / cell_height as usize;
|
|
|
|
|
|
|
|
let padding_left = config.window_padding.left as f32;
|
|
|
|
let padding_top = config.window_padding.top as f32;
|
2019-10-27 02:59:53 +03:00
|
|
|
|
2019-12-01 22:43:22 +03:00
|
|
|
log::debug!(
|
2019-12-22 01:17:50 +03:00
|
|
|
"compute_vertices {}x{} {}x{} padding={} {}",
|
2019-12-01 10:22:37 +03:00
|
|
|
num_cols,
|
|
|
|
num_rows,
|
|
|
|
width,
|
2019-12-22 01:17:50 +03:00
|
|
|
height,
|
|
|
|
padding_left,
|
|
|
|
padding_top
|
2019-12-01 10:22:37 +03:00
|
|
|
);
|
|
|
|
|
2019-12-24 06:13:15 +03:00
|
|
|
let mut quads = Quads::default();
|
|
|
|
quads.cols = num_cols;
|
2019-12-23 05:57:54 +03:00
|
|
|
|
2019-12-24 06:13:15 +03:00
|
|
|
let mut define_quad = |left, top, right, bottom| -> u32 {
|
2019-12-23 05:57:54 +03:00
|
|
|
// Remember starting index for this position
|
|
|
|
let idx = verts.len() as u32;
|
2019-12-24 06:13:15 +03:00
|
|
|
|
2019-12-23 05:57:54 +03:00
|
|
|
verts.push(Vertex {
|
|
|
|
// Top left
|
2019-12-24 06:13:15 +03:00
|
|
|
position: (left, top),
|
2019-12-23 05:57:54 +03:00
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
verts.push(Vertex {
|
|
|
|
// Top Right
|
2019-12-24 06:13:15 +03:00
|
|
|
position: (right, top),
|
2019-12-23 05:57:54 +03:00
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
verts.push(Vertex {
|
|
|
|
// Bottom Left
|
2019-12-24 06:13:15 +03:00
|
|
|
position: (left, bottom),
|
2019-12-23 05:57:54 +03:00
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
verts.push(Vertex {
|
|
|
|
// Bottom Right
|
2019-12-24 06:13:15 +03:00
|
|
|
position: (right, bottom),
|
2019-12-23 05:57:54 +03:00
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
idx
|
|
|
|
};
|
|
|
|
|
2020-10-18 19:47:55 +03:00
|
|
|
// Background image fills the entire window background
|
|
|
|
quads.background_image =
|
|
|
|
define_quad(width / -2.0, height / -2.0, width / 2.0, height / 2.0) as usize;
|
|
|
|
|
2020-10-19 03:50:49 +03:00
|
|
|
for y in 0..=num_rows {
|
2019-12-24 06:13:15 +03:00
|
|
|
let y_pos = (height / -2.0) + (y as f32 * cell_height) + padding_top;
|
|
|
|
|
|
|
|
for x in 0..num_cols {
|
|
|
|
let x_pos = (width / -2.0) + (x as f32 * cell_width) + padding_left;
|
|
|
|
|
|
|
|
let idx = define_quad(x_pos, y_pos, x_pos + cell_width, y_pos + cell_height);
|
|
|
|
if x == 0 {
|
|
|
|
// build row -> vertex mapping
|
|
|
|
quads.row_starts.push(idx as usize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-20 06:59:22 +03:00
|
|
|
// And a quad for the scrollbar thumb
|
|
|
|
quads.scroll_thumb = define_quad(0.0, 0.0, 0.0, 0.0) as usize;
|
2019-12-23 05:57:54 +03:00
|
|
|
|
2019-10-27 02:59:53 +03:00
|
|
|
Ok((
|
|
|
|
VertexBuffer::dynamic(context, &verts)?,
|
|
|
|
IndexBuffer::new(
|
|
|
|
context,
|
|
|
|
glium::index::PrimitiveType::TrianglesList,
|
|
|
|
&indices,
|
|
|
|
)?,
|
2019-12-24 06:13:15 +03:00
|
|
|
quads,
|
2019-10-27 02:59:53 +03:00
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
|
|
pub enum RenderState {
|
|
|
|
Software(SoftwareRenderState),
|
|
|
|
GL(OpenGLRenderState),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderState {
|
2020-10-23 23:57:58 +03:00
|
|
|
pub fn clear_texture_atlas(&mut self, metrics: &RenderMetrics) -> anyhow::Result<()> {
|
|
|
|
match self {
|
|
|
|
RenderState::Software(_) => {}
|
|
|
|
RenderState::GL(gl) => {
|
|
|
|
let mut glyph_cache = gl.glyph_cache.borrow_mut();
|
|
|
|
glyph_cache.clear();
|
|
|
|
gl.util_sprites = UtilSprites::new(&mut glyph_cache, metrics)?;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-10-27 02:59:53 +03:00
|
|
|
pub fn recreate_texture_atlas(
|
|
|
|
&mut self,
|
|
|
|
fonts: &Rc<FontConfiguration>,
|
|
|
|
metrics: &RenderMetrics,
|
|
|
|
size: Option<usize>,
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<()> {
|
2019-10-27 02:59:53 +03:00
|
|
|
match self {
|
2020-10-19 19:22:02 +03:00
|
|
|
RenderState::Software(_) => {}
|
2019-10-27 02:59:53 +03:00
|
|
|
RenderState::GL(gl) => {
|
|
|
|
let size = size.unwrap_or_else(|| gl.glyph_cache.borrow().atlas.size());
|
2021-01-06 01:16:21 +03:00
|
|
|
let mut glyph_cache = GlyphCache::new_gl(&gl.context, fonts, size, metrics)?;
|
2019-10-27 02:59:53 +03:00
|
|
|
gl.util_sprites = UtilSprites::new(&mut glyph_cache, metrics)?;
|
|
|
|
*gl.glyph_cache.borrow_mut() = glyph_cache;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn advise_of_window_size_change(
|
|
|
|
&mut self,
|
|
|
|
metrics: &RenderMetrics,
|
|
|
|
pixel_width: usize,
|
|
|
|
pixel_height: usize,
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<()> {
|
2019-10-27 02:59:53 +03:00
|
|
|
if let RenderState::GL(gl) = self {
|
|
|
|
gl.advise_of_window_size_change(metrics, pixel_width, pixel_height)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-12-05 21:43:20 +03:00
|
|
|
pub fn has_opengl(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
RenderState::GL(_) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-27 02:59:53 +03:00
|
|
|
pub fn opengl(&self) -> &OpenGLRenderState {
|
|
|
|
match self {
|
|
|
|
RenderState::GL(gl) => gl,
|
|
|
|
_ => panic!("only valid for opengl render mode"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|