diff --git a/src/frontend/gui/quad.rs b/src/frontend/gui/quad.rs index 582d5ebdb..b9d6dcaf9 100644 --- a/src/frontend/gui/quad.rs +++ b/src/frontend/gui/quad.rs @@ -3,7 +3,9 @@ #![allow(clippy::unneeded_field_pattern)] use ::window::bitmaps::TextureRect; +use ::window::glium::VertexBuffer; use ::window::*; +use std::cell::RefMut; /// Each cell is composed of two triangles built from 4 vertices. /// The buffer is organized row by row. @@ -44,20 +46,65 @@ pub struct Vertex { has_color ); +/// A helper for knowing how to locate the right quad for an element +/// in the UI +#[derive(Default, Debug, Clone)] +pub struct Quads { + /// How many cells per row + pub cols: usize, + /// row number to vertex index for the first vertex on that row + pub row_starts: Vec, + /// The vertex index for the first vertex of the scroll bar thumb + pub scroll_thumb: usize, +} + +pub struct MappedQuads<'a> { + mapping: glium::buffer::Mapping<'a, [Vertex]>, + quads: Quads, +} + +impl<'a> MappedQuads<'a> { + pub fn cell<'b>(&'b mut self, x: usize, y: usize) -> anyhow::Result> { + if x >= self.quads.cols { + anyhow::bail!("column {} is outside of the vertex buffer range", x); + } + + let start = self + .quads + .row_starts + .get(y) + .ok_or_else(|| anyhow::anyhow!("line {} is outside the vertex buffer range", y))? + + x * VERTICES_PER_CELL; + + Ok(Quad { + vert: &mut self.mapping[start..start + VERTICES_PER_CELL], + }) + } + + pub fn scroll_thumb<'b>(&'b mut self) -> Quad<'b> { + let start = self.quads.scroll_thumb; + Quad { + vert: &mut self.mapping[start..start + VERTICES_PER_CELL], + } + } +} + +impl Quads { + pub fn map<'a>(&self, vb: &'a mut RefMut>) -> MappedQuads<'a> { + let mapping = vb.slice_mut(..).expect("to map vertex buffer").map(); + MappedQuads { + mapping, + quads: self.clone(), + } + } +} + /// A helper for updating the 4 vertices that compose a glyph cell pub struct Quad<'a> { vert: &'a mut [Vertex], } impl<'a> Quad<'a> { - /// Returns a reference to the Quad for the given cell column index - /// into the set of vertices for a line. - pub fn for_cell(cell_idx: usize, vertices: &'a mut [Vertex]) -> Self { - let vert_idx = cell_idx * VERTICES_PER_CELL; - let vert = &mut vertices[vert_idx..vert_idx + VERTICES_PER_CELL]; - Self { vert } - } - /// Assign the texture coordinates pub fn set_texture(&mut self, coords: TextureRect) { self.vert[V_TOP_LEFT].tex = (coords.min_x(), coords.min_y()); diff --git a/src/frontend/gui/renderstate.rs b/src/frontend/gui/renderstate.rs index 925969591..e0f2bc8e9 100644 --- a/src/frontend/gui/renderstate.rs +++ b/src/frontend/gui/renderstate.rs @@ -1,4 +1,5 @@ use super::glyphcache::{CachedGlyph, GlyphCache}; +use super::quad::*; use super::utilsprites::{RenderMetrics, UtilSprites}; use crate::config::{configuration, TextStyle}; use crate::font::{FontConfiguration, GlyphInfo}; @@ -11,8 +12,6 @@ use anyhow::{anyhow, bail}; use std::cell::RefCell; use std::rc::Rc; -use super::quad::*; - pub struct SoftwareRenderState { pub glyph_cache: RefCell>, pub util_sprites: UtilSprites, @@ -40,6 +39,7 @@ pub struct OpenGLRenderState { pub program: glium::Program, pub glyph_vertex_buffer: RefCell>, pub glyph_index_buffer: IndexBuffer, + pub quads: Quads, } impl OpenGLRenderState { @@ -80,7 +80,7 @@ impl OpenGLRenderState { let program = program.ok_or_else(|| anyhow!("Failed to compile shaders: {}", errors.join("\n")))?; - let (glyph_vertex_buffer, glyph_index_buffer) = + let (glyph_vertex_buffer, glyph_index_buffer, quads) = Self::compute_vertices(&context, metrics, pixel_width as f32, pixel_height as f32)?; Ok(Self { @@ -90,6 +90,7 @@ impl OpenGLRenderState { program, glyph_vertex_buffer: RefCell::new(glyph_vertex_buffer), glyph_index_buffer, + quads, }) } @@ -99,7 +100,7 @@ impl OpenGLRenderState { pixel_width: usize, pixel_height: usize, ) -> anyhow::Result<()> { - let (glyph_vertex_buffer, glyph_index_buffer) = Self::compute_vertices( + let (glyph_vertex_buffer, glyph_index_buffer, quads) = Self::compute_vertices( &self.context, metrics, pixel_width as f32, @@ -108,6 +109,7 @@ impl OpenGLRenderState { *self.glyph_vertex_buffer.borrow_mut() = glyph_vertex_buffer; self.glyph_index_buffer = glyph_index_buffer; + self.quads = quads; Ok(()) } @@ -130,7 +132,7 @@ impl OpenGLRenderState { metrics: &RenderMetrics, width: f32, height: f32, - ) -> anyhow::Result<(VertexBuffer, IndexBuffer)> { + ) -> anyhow::Result<(VertexBuffer, IndexBuffer, Quads)> { let cell_width = metrics.cell_size.width as f32; let cell_height = metrics.cell_size.height as f32; let mut verts = Vec::new(); @@ -159,72 +161,31 @@ impl OpenGLRenderState { padding_top ); - for y in 0..num_rows { - 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; - - // Remember starting index for this position - let idx = verts.len() as u32; - verts.push(Vertex { - // Top left - position: (x_pos, y_pos), - ..Default::default() - }); - verts.push(Vertex { - // Top Right - position: (x_pos + cell_width, y_pos), - ..Default::default() - }); - verts.push(Vertex { - // Bottom Left - position: (x_pos, y_pos + cell_height), - ..Default::default() - }); - verts.push(Vertex { - // Bottom Right - position: (x_pos + cell_width, y_pos + cell_height), - ..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); - } - } - - { - // And a quad for the scrollbar thumb - let x_pos = (width / 2.0) - cell_width; - let y_pos = (height / -2.0) + padding_top; - let thumb_width = cell_width; - let thumb_height = height; + let mut quads = Quads::default(); + quads.cols = num_cols; + let mut define_quad = |left, top, right, bottom| -> u32 { // Remember starting index for this position let idx = verts.len() as u32; + verts.push(Vertex { // Top left - position: (x_pos, thumb_width), + position: (left, top), ..Default::default() }); verts.push(Vertex { // Top Right - position: (x_pos + thumb_width, y_pos), + position: (right, top), ..Default::default() }); verts.push(Vertex { // Bottom Left - position: (x_pos, y_pos + thumb_height), + position: (left, bottom), ..Default::default() }); verts.push(Vertex { // Bottom Right - position: (x_pos + thumb_width, y_pos + thumb_height), + position: (right, bottom), ..Default::default() }); @@ -236,6 +197,33 @@ impl OpenGLRenderState { indices.push(idx + V_TOP_RIGHT as u32); indices.push(idx + V_BOT_LEFT as u32); indices.push(idx + V_BOT_RIGHT as u32); + + idx + }; + + for y in 0..num_rows { + 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); + } + } + } + + { + // And a quad for the scrollbar thumb + let x_pos = (width / 2.0) - cell_width; + let y_pos = (height / -2.0) + padding_top; + let thumb_width = cell_width; + let thumb_height = height; + + quads.scroll_thumb = + define_quad(x_pos, y_pos, x_pos + thumb_width, y_pos + thumb_height) as usize; } Ok(( @@ -245,6 +233,7 @@ impl OpenGLRenderState { glium::index::PrimitiveType::TrianglesList, &indices, )?, + quads, )) } } diff --git a/src/frontend/gui/termwindow.rs b/src/frontend/gui/termwindow.rs index a8444a64b..d92e64dc2 100644 --- a/src/frontend/gui/termwindow.rs +++ b/src/frontend/gui/termwindow.rs @@ -1403,6 +1403,10 @@ impl TermWindow { } }; + let gl_state = self.render_state.opengl(); + let mut vb = gl_state.glyph_vertex_buffer.borrow_mut(); + let mut quads = gl_state.quads.map(&mut vb); + if self.show_tab_bar { self.render_screen_line_opengl( 0, @@ -1411,6 +1415,7 @@ impl TermWindow { &cursor, &*term, &palette, + &mut quads, )?; } @@ -1419,13 +1424,7 @@ impl TermWindow { let thumb_top = info.top as f32; let thumb_size = info.height as f32; - let gl_state = self.render_state.opengl(); - - // We reserved the final quad in the vertex buffer as the scrollbar - let mut vb = gl_state.glyph_vertex_buffer.borrow_mut(); - let num_vert = vb.len() - VERTICES_PER_CELL; - let mut vertices = &mut vb.slice_mut(num_vert..).unwrap().map(); - let mut quad = Quad::for_cell(0, &mut vertices); + let mut quad = quads.scroll_thumb(); // Adjust the scrollbar thumb position let top = (self.dimensions.pixel_height as f32 / -2.0) + thumb_top; @@ -1454,11 +1453,11 @@ impl TermWindow { &cursor, &*term, &palette, + &mut quads, )?; } } - let gl_state = self.render_state.opengl(); let tex = gl_state.glyph_cache.borrow().atlas.texture(); let projection = euclid::Transform3D::::ortho( -(self.dimensions.pixel_width as f32) / 2.0, @@ -1475,9 +1474,11 @@ impl TermWindow { ..Default::default() }; + drop(quads); + // Pass 1: Draw backgrounds, strikethrough and underline frame.draw( - &*gl_state.glyph_vertex_buffer.borrow(), + &*vb, &gl_state.glyph_index_buffer, &gl_state.program, &uniform! { @@ -1490,7 +1491,7 @@ impl TermWindow { // Pass 2: Draw glyphs frame.draw( - &*gl_state.glyph_vertex_buffer.borrow(), + &*vb, &gl_state.glyph_index_buffer, &gl_state.program, &uniform! { @@ -1518,27 +1519,11 @@ impl TermWindow { cursor: &CursorPosition, terminal: &dyn Renderable, palette: &ColorPalette, + quads: &mut MappedQuads, ) -> anyhow::Result<()> { let gl_state = self.render_state.opengl(); let (_num_rows, num_cols) = terminal.physical_dimensions(); - let mut vb = gl_state.glyph_vertex_buffer.borrow_mut(); - let mut vertices = { - let per_line = num_cols * VERTICES_PER_CELL; - let start_pos = line_idx * per_line; - vb.slice_mut(start_pos..start_pos + per_line) - .ok_or_else(|| { - anyhow!( - "we're confused about the screen size; \ - line_idx={} start_pos={} per_line={} num_cols={}", - line_idx, - start_pos, - per_line, - num_cols - ) - })? - .map() - }; let current_highlight = terminal.current_highlight(); let cursor_border_color = rgbcolor_to_window_color(palette.cursor_border); @@ -1673,7 +1658,7 @@ impl TermWindow { let texture_rect = sprite.texture.to_texture_coords(coords); - let mut quad = Quad::for_cell(cell_idx, &mut vertices); + let mut quad = quads.cell(cell_idx, line_idx)?; quad.set_fg_color(glyph_color); quad.set_bg_color(bg_color); @@ -1715,7 +1700,7 @@ impl TermWindow { let right = pixel_rect.size.width as f32 + left - self.render_metrics.cell_size.width as f32; - let mut quad = Quad::for_cell(cell_idx, &mut vertices); + let mut quad = quads.cell(cell_idx, line_idx)?; quad.set_fg_color(glyph_color); quad.set_bg_color(bg_color); @@ -1757,7 +1742,7 @@ impl TermWindow { palette, ); - let mut quad = Quad::for_cell(cell_idx, &mut vertices); + let mut quad = quads.cell(cell_idx, line_idx)?; quad.set_bg_color(bg_color); quad.set_fg_color(glyph_color);