|
|
|
@@ -381,6 +381,8 @@ pub struct WebGlRenderStats {
|
|
|
|
pub cells: usize,
|
|
|
|
pub cells: usize,
|
|
|
|
pub row_cache_hits: usize,
|
|
|
|
pub row_cache_hits: usize,
|
|
|
|
pub row_cache_misses: usize,
|
|
|
|
pub row_cache_misses: usize,
|
|
|
|
|
|
|
|
pub vertex_cache_hits: usize,
|
|
|
|
|
|
|
|
pub vertex_cache_misses: usize,
|
|
|
|
pub atlas_entries: usize,
|
|
|
|
pub atlas_entries: usize,
|
|
|
|
pub atlas_insertions: usize,
|
|
|
|
pub atlas_insertions: usize,
|
|
|
|
pub atlas_resets: usize,
|
|
|
|
pub atlas_resets: usize,
|
|
|
|
@@ -569,6 +571,13 @@ struct GlyphCell {
|
|
|
|
fg: [f32; 4],
|
|
|
|
fg: [f32; 4],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
|
|
|
|
struct VisibleGlyphRow {
|
|
|
|
|
|
|
|
cache_key: u64,
|
|
|
|
|
|
|
|
row: usize,
|
|
|
|
|
|
|
|
cell_count: usize,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
struct CachedGlyphCell {
|
|
|
|
struct CachedGlyphCell {
|
|
|
|
key: GlyphKey,
|
|
|
|
key: GlyphKey,
|
|
|
|
@@ -580,6 +589,12 @@ struct CachedGlyphCell {
|
|
|
|
struct CachedRowGlyphs {
|
|
|
|
struct CachedRowGlyphs {
|
|
|
|
key: u64,
|
|
|
|
key: u64,
|
|
|
|
cells: Vec<CachedGlyphCell>,
|
|
|
|
cells: Vec<CachedGlyphCell>,
|
|
|
|
|
|
|
|
vertex_template: Option<CachedRowVertexTemplate>,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
|
|
|
struct CachedRowVertexTemplate {
|
|
|
|
|
|
|
|
vertices: Vec<f32>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
@@ -591,6 +606,8 @@ struct WebGlRowCache {
|
|
|
|
struct WebGlRowCacheStats {
|
|
|
|
struct WebGlRowCacheStats {
|
|
|
|
hits: usize,
|
|
|
|
hits: usize,
|
|
|
|
misses: usize,
|
|
|
|
misses: usize,
|
|
|
|
|
|
|
|
vertex_hits: usize,
|
|
|
|
|
|
|
|
vertex_misses: usize,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
|
|
@@ -655,24 +672,26 @@ fn split_segment_glyphs(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn collect_glyph_cells(
|
|
|
|
fn collect_glyph_rows(
|
|
|
|
visible: &[TerminalRow],
|
|
|
|
visible: &[TerminalRow],
|
|
|
|
options: &ResolvedTerminalOptions,
|
|
|
|
options: &ResolvedTerminalOptions,
|
|
|
|
cache: &mut WebGlRowCache,
|
|
|
|
cache: &mut WebGlRowCache,
|
|
|
|
) -> Option<(Vec<GlyphCell>, WebGlRowCacheStats)> {
|
|
|
|
) -> Option<(Vec<VisibleGlyphRow>, usize, WebGlRowCacheStats)> {
|
|
|
|
let mut cells = Vec::new();
|
|
|
|
let mut rows = Vec::new();
|
|
|
|
|
|
|
|
let mut cell_count = 0usize;
|
|
|
|
let mut stats = WebGlRowCacheStats::default();
|
|
|
|
let mut stats = WebGlRowCacheStats::default();
|
|
|
|
for (row_idx, row) in visible.iter().enumerate() {
|
|
|
|
for (row_idx, row) in visible.iter().enumerate() {
|
|
|
|
let cached = cache.glyphs_for_row(row, options, &mut stats)?;
|
|
|
|
let (cache_key, cached) = cache.glyphs_for_row(row, options, &mut stats)?;
|
|
|
|
cells.extend(cached.iter().map(|cell| GlyphCell {
|
|
|
|
let row_cell_count = cached.len();
|
|
|
|
key: cell.key.clone(),
|
|
|
|
cell_count += row_cell_count;
|
|
|
|
|
|
|
|
rows.push(VisibleGlyphRow {
|
|
|
|
|
|
|
|
cache_key,
|
|
|
|
row: row_idx,
|
|
|
|
row: row_idx,
|
|
|
|
col: cell.col,
|
|
|
|
cell_count: row_cell_count,
|
|
|
|
fg: cell.fg,
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cache.retain_recent(visible.len());
|
|
|
|
cache.retain_recent(visible.len());
|
|
|
|
Some((cells, stats))
|
|
|
|
Some((rows, cell_count, stats))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl WebGlRowCache {
|
|
|
|
impl WebGlRowCache {
|
|
|
|
@@ -681,7 +700,7 @@ impl WebGlRowCache {
|
|
|
|
row: &TerminalRow,
|
|
|
|
row: &TerminalRow,
|
|
|
|
options: &ResolvedTerminalOptions,
|
|
|
|
options: &ResolvedTerminalOptions,
|
|
|
|
stats: &mut WebGlRowCacheStats,
|
|
|
|
stats: &mut WebGlRowCacheStats,
|
|
|
|
) -> Option<&[CachedGlyphCell]> {
|
|
|
|
) -> Option<(u64, &[CachedGlyphCell])> {
|
|
|
|
let key = row_glyph_cache_key(row, options);
|
|
|
|
let key = row_glyph_cache_key(row, options);
|
|
|
|
if let Some(index) = self.glyph_rows.iter().position(|cached| cached.key == key) {
|
|
|
|
if let Some(index) = self.glyph_rows.iter().position(|cached| cached.key == key) {
|
|
|
|
stats.hits += 1;
|
|
|
|
stats.hits += 1;
|
|
|
|
@@ -689,13 +708,20 @@ impl WebGlRowCache {
|
|
|
|
let cached = self.glyph_rows.remove(index);
|
|
|
|
let cached = self.glyph_rows.remove(index);
|
|
|
|
self.glyph_rows.insert(0, cached);
|
|
|
|
self.glyph_rows.insert(0, cached);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Some(&self.glyph_rows[0].cells);
|
|
|
|
return Some((key, &self.glyph_rows[0].cells));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let cells = collect_row_glyph_cells(row, options)?;
|
|
|
|
let cells = collect_row_glyph_cells(row, options)?;
|
|
|
|
stats.misses += 1;
|
|
|
|
stats.misses += 1;
|
|
|
|
self.glyph_rows.insert(0, CachedRowGlyphs { key, cells });
|
|
|
|
self.glyph_rows.insert(
|
|
|
|
Some(&self.glyph_rows[0].cells)
|
|
|
|
0,
|
|
|
|
|
|
|
|
CachedRowGlyphs {
|
|
|
|
|
|
|
|
key,
|
|
|
|
|
|
|
|
cells,
|
|
|
|
|
|
|
|
vertex_template: None,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
Some((key, &self.glyph_rows[0].cells))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn retain_recent(&mut self, visible_rows: usize) {
|
|
|
|
fn retain_recent(&mut self, visible_rows: usize) {
|
|
|
|
@@ -704,6 +730,27 @@ impl WebGlRowCache {
|
|
|
|
self.glyph_rows.truncate(max_rows);
|
|
|
|
self.glyph_rows.truncate(max_rows);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn cells_for_row(&self, key: u64) -> Option<&[CachedGlyphCell]> {
|
|
|
|
|
|
|
|
self.glyph_rows
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.find(|row| row.key == key)
|
|
|
|
|
|
|
|
.map(|row| row.cells.as_slice())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn vertex_template(&self, key: u64) -> Option<&[f32]> {
|
|
|
|
|
|
|
|
self.glyph_rows
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.find(|row| row.key == key)
|
|
|
|
|
|
|
|
.and_then(|row| row.vertex_template.as_ref())
|
|
|
|
|
|
|
|
.map(|template| template.vertices.as_slice())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn store_vertex_template(&mut self, key: u64, template: CachedRowVertexTemplate) {
|
|
|
|
|
|
|
|
if let Some(row) = self.glyph_rows.iter_mut().find(|row| row.key == key) {
|
|
|
|
|
|
|
|
row.vertex_template = Some(template);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn collect_row_glyph_cells(
|
|
|
|
fn collect_row_glyph_cells(
|
|
|
|
@@ -923,8 +970,8 @@ pub(super) fn render_webgl(
|
|
|
|
reset_webgl_caches_if_needed(state, frame);
|
|
|
|
reset_webgl_caches_if_needed(state, frame);
|
|
|
|
|
|
|
|
|
|
|
|
let collect_start = perf_now();
|
|
|
|
let collect_start = perf_now();
|
|
|
|
let Some((cells, row_cache_stats)) =
|
|
|
|
let Some((glyph_rows, cell_count, row_cache_stats)) =
|
|
|
|
collect_glyph_cells(frame.visible, frame.options, &mut state.row_cache)
|
|
|
|
collect_glyph_rows(frame.visible, frame.options, &mut state.row_cache)
|
|
|
|
else {
|
|
|
|
else {
|
|
|
|
stats.collect_ms = perf_now() - collect_start;
|
|
|
|
stats.collect_ms = perf_now() - collect_start;
|
|
|
|
render_webgl_canvas_fallback(state, frame, &mut stats)?;
|
|
|
|
render_webgl_canvas_fallback(state, frame, &mut stats)?;
|
|
|
|
@@ -938,14 +985,14 @@ pub(super) fn render_webgl(
|
|
|
|
return Ok(stats);
|
|
|
|
return Ok(stats);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
stats.collect_ms = perf_now() - collect_start;
|
|
|
|
stats.collect_ms = perf_now() - collect_start;
|
|
|
|
stats.cells = cells.len();
|
|
|
|
stats.cells = cell_count;
|
|
|
|
stats.row_cache_hits = row_cache_stats.hits;
|
|
|
|
stats.row_cache_hits = row_cache_stats.hits;
|
|
|
|
stats.row_cache_misses = row_cache_stats.misses;
|
|
|
|
stats.row_cache_misses = row_cache_stats.misses;
|
|
|
|
|
|
|
|
|
|
|
|
let backdrop_start = perf_now();
|
|
|
|
let backdrop_start = perf_now();
|
|
|
|
draw_webgl_backdrop_quads(state, frame.dom_state, &backdrop_quads)?;
|
|
|
|
draw_webgl_backdrop_quads(state, frame.dom_state, &backdrop_quads)?;
|
|
|
|
stats.backdrop_ms = perf_now() - backdrop_start;
|
|
|
|
stats.backdrop_ms = perf_now() - backdrop_start;
|
|
|
|
if draw_webgl_glyphs(state, frame, &cells, &mut stats).is_err() {
|
|
|
|
if draw_webgl_glyphs(state, frame, &glyph_rows, &mut stats).is_err() {
|
|
|
|
stats.glyph_draw_failed = true;
|
|
|
|
stats.glyph_draw_failed = true;
|
|
|
|
render_webgl_canvas_fallback(state, frame, &mut stats)?;
|
|
|
|
render_webgl_canvas_fallback(state, frame, &mut stats)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -1323,11 +1370,11 @@ fn draw_webgl_backdrop_quads(
|
|
|
|
fn draw_webgl_glyphs(
|
|
|
|
fn draw_webgl_glyphs(
|
|
|
|
state: &mut WebGlRendererState,
|
|
|
|
state: &mut WebGlRendererState,
|
|
|
|
frame: CanvasSurfaceFrame<'_>,
|
|
|
|
frame: CanvasSurfaceFrame<'_>,
|
|
|
|
cells: &[GlyphCell],
|
|
|
|
rows: &[VisibleGlyphRow],
|
|
|
|
stats: &mut WebGlRenderStats,
|
|
|
|
stats: &mut WebGlRenderStats,
|
|
|
|
) -> Result<(), String> {
|
|
|
|
) -> Result<(), String> {
|
|
|
|
let atlas_start = perf_now();
|
|
|
|
let atlas_start = perf_now();
|
|
|
|
ensure_glyphs_in_atlas(state, frame, cells, stats)?;
|
|
|
|
ensure_glyph_rows_in_atlas(state, frame, rows, stats)?;
|
|
|
|
stats.atlas_ms += perf_now() - atlas_start;
|
|
|
|
stats.atlas_ms += perf_now() - atlas_start;
|
|
|
|
if state.atlas.dirty {
|
|
|
|
if state.atlas.dirty {
|
|
|
|
let upload_start = perf_now();
|
|
|
|
let upload_start = perf_now();
|
|
|
|
@@ -1351,26 +1398,8 @@ fn draw_webgl_glyphs(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let vertex_start = perf_now();
|
|
|
|
let vertex_start = perf_now();
|
|
|
|
let mut vertices = Vec::with_capacity(cells.len() * WEBGL_VERTEX_FLOATS * 6);
|
|
|
|
let mut vertices = Vec::with_capacity(stats.cells * WEBGL_VERTEX_FLOATS * 6);
|
|
|
|
for cell in cells {
|
|
|
|
materialize_glyph_row_vertices(state, frame, rows, &mut vertices, stats)?;
|
|
|
|
let Some(entry) = state.atlas.get(&cell.key) else {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
let x = cell.col as f64 * frame.cell_size.0;
|
|
|
|
|
|
|
|
let y = frame.content_top_px + cell.row as f64 * frame.cell_size.1;
|
|
|
|
|
|
|
|
push_quad(
|
|
|
|
|
|
|
|
&mut vertices,
|
|
|
|
|
|
|
|
GlyphQuad {
|
|
|
|
|
|
|
|
x,
|
|
|
|
|
|
|
|
y,
|
|
|
|
|
|
|
|
width: entry.cols as f64 * frame.cell_size.0,
|
|
|
|
|
|
|
|
height: frame.cell_size.1,
|
|
|
|
|
|
|
|
uv: glyph_uv(entry, state.atlas.size),
|
|
|
|
|
|
|
|
color: cell.fg,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
frame.dom_state,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
stats.vertex_build_ms += perf_now() - vertex_start;
|
|
|
|
stats.vertex_build_ms += perf_now() - vertex_start;
|
|
|
|
if vertices.is_empty() {
|
|
|
|
if vertices.is_empty() {
|
|
|
|
return Ok(());
|
|
|
|
return Ok(());
|
|
|
|
@@ -1398,10 +1427,79 @@ fn draw_webgl_glyphs(
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn ensure_glyphs_in_atlas(
|
|
|
|
fn materialize_glyph_row_vertices(
|
|
|
|
state: &mut WebGlRendererState,
|
|
|
|
state: &mut WebGlRendererState,
|
|
|
|
frame: CanvasSurfaceFrame<'_>,
|
|
|
|
frame: CanvasSurfaceFrame<'_>,
|
|
|
|
cells: &[GlyphCell],
|
|
|
|
rows: &[VisibleGlyphRow],
|
|
|
|
|
|
|
|
vertices: &mut Vec<f32>,
|
|
|
|
|
|
|
|
stats: &mut WebGlRenderStats,
|
|
|
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
|
|
|
for row in rows {
|
|
|
|
|
|
|
|
let y = frame.content_top_px + row.row as f64 * frame.cell_size.1;
|
|
|
|
|
|
|
|
if let Some(template) = state.row_cache.vertex_template(row.cache_key) {
|
|
|
|
|
|
|
|
stats.vertex_cache_hits += 1;
|
|
|
|
|
|
|
|
append_row_vertex_template(vertices, template, y, frame.dom_state);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stats.vertex_cache_misses += 1;
|
|
|
|
|
|
|
|
let Some(cells) = state.row_cache.cells_for_row(row.cache_key) else {
|
|
|
|
|
|
|
|
return Err("missing cached glyph row".to_owned());
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
let template = build_row_vertex_template(&state.atlas, frame, cells)?;
|
|
|
|
|
|
|
|
append_row_vertex_template(vertices, &template.vertices, y, frame.dom_state);
|
|
|
|
|
|
|
|
state
|
|
|
|
|
|
|
|
.row_cache
|
|
|
|
|
|
|
|
.store_vertex_template(row.cache_key, template);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn build_row_vertex_template(
|
|
|
|
|
|
|
|
atlas: &GlyphAtlas,
|
|
|
|
|
|
|
|
frame: CanvasSurfaceFrame<'_>,
|
|
|
|
|
|
|
|
cells: &[CachedGlyphCell],
|
|
|
|
|
|
|
|
) -> Result<CachedRowVertexTemplate, String> {
|
|
|
|
|
|
|
|
let mut vertices = Vec::with_capacity(cells.len() * WEBGL_VERTEX_FLOATS * 6);
|
|
|
|
|
|
|
|
for cell in cells {
|
|
|
|
|
|
|
|
let Some(entry) = atlas.get(&cell.key) else {
|
|
|
|
|
|
|
|
return Err("missing glyph atlas entry".to_owned());
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
let x = cell.col as f64 * frame.cell_size.0;
|
|
|
|
|
|
|
|
push_quad(
|
|
|
|
|
|
|
|
&mut vertices,
|
|
|
|
|
|
|
|
GlyphQuad {
|
|
|
|
|
|
|
|
x,
|
|
|
|
|
|
|
|
y: 0.0,
|
|
|
|
|
|
|
|
width: entry.cols as f64 * frame.cell_size.0,
|
|
|
|
|
|
|
|
height: frame.cell_size.1,
|
|
|
|
|
|
|
|
uv: glyph_uv(entry, atlas.size),
|
|
|
|
|
|
|
|
color: cell.fg,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
frame.dom_state,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(CachedRowVertexTemplate { vertices })
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn append_row_vertex_template(
|
|
|
|
|
|
|
|
vertices: &mut Vec<f32>,
|
|
|
|
|
|
|
|
template: &[f32],
|
|
|
|
|
|
|
|
y: f64,
|
|
|
|
|
|
|
|
dom_state: CanvasDomState,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
let y_offset = (-y / dom_state.css_height as f64 * 2.0) as f32;
|
|
|
|
|
|
|
|
let start = vertices.len();
|
|
|
|
|
|
|
|
vertices.extend_from_slice(template);
|
|
|
|
|
|
|
|
for index in (start..vertices.len()).step_by(WEBGL_VERTEX_FLOATS) {
|
|
|
|
|
|
|
|
vertices[index + 1] += y_offset;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn ensure_glyph_rows_in_atlas(
|
|
|
|
|
|
|
|
state: &mut WebGlRendererState,
|
|
|
|
|
|
|
|
frame: CanvasSurfaceFrame<'_>,
|
|
|
|
|
|
|
|
rows: &[VisibleGlyphRow],
|
|
|
|
stats: &mut WebGlRenderStats,
|
|
|
|
stats: &mut WebGlRenderStats,
|
|
|
|
) -> Result<(), String> {
|
|
|
|
) -> Result<(), String> {
|
|
|
|
for attempt in 0..2 {
|
|
|
|
for attempt in 0..2 {
|
|
|
|
@@ -1415,11 +1513,11 @@ fn ensure_glyphs_in_atlas(
|
|
|
|
f64::from(state.atlas.size),
|
|
|
|
f64::from(state.atlas.size),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let missing =
|
|
|
|
|
|
|
|
missing_glyph_keys(state, rows).ok_or_else(|| "missing cached glyph row".to_owned())?;
|
|
|
|
let mut ok = true;
|
|
|
|
let mut ok = true;
|
|
|
|
for cell in cells {
|
|
|
|
for key in missing {
|
|
|
|
if state.atlas.get(&cell.key).is_none()
|
|
|
|
if draw_glyph_into_atlas(state, frame, &key, stats)?.is_none() {
|
|
|
|
&& draw_glyph_into_atlas(state, frame, &cell.key, stats)?.is_none()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
ok = false;
|
|
|
|
ok = false;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -1431,6 +1529,22 @@ fn ensure_glyphs_in_atlas(
|
|
|
|
Err("glyph atlas is full".to_owned())
|
|
|
|
Err("glyph atlas is full".to_owned())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn missing_glyph_keys(
|
|
|
|
|
|
|
|
state: &WebGlRendererState,
|
|
|
|
|
|
|
|
rows: &[VisibleGlyphRow],
|
|
|
|
|
|
|
|
) -> Option<Vec<GlyphKey>> {
|
|
|
|
|
|
|
|
let mut missing = Vec::new();
|
|
|
|
|
|
|
|
for row in rows {
|
|
|
|
|
|
|
|
let cells = state.row_cache.cells_for_row(row.cache_key)?;
|
|
|
|
|
|
|
|
for cell in cells {
|
|
|
|
|
|
|
|
if state.atlas.get(&cell.key).is_none() {
|
|
|
|
|
|
|
|
missing.push(cell.key.clone());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(missing)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn draw_glyph_into_atlas(
|
|
|
|
fn draw_glyph_into_atlas(
|
|
|
|
state: &mut WebGlRendererState,
|
|
|
|
state: &mut WebGlRendererState,
|
|
|
|
frame: CanvasSurfaceFrame<'_>,
|
|
|
|
frame: CanvasSurfaceFrame<'_>,
|
|
|
|
@@ -1767,11 +1881,13 @@ mod tests {
|
|
|
|
let first = cache
|
|
|
|
let first = cache
|
|
|
|
.glyphs_for_row(&row, &options, &mut stats)
|
|
|
|
.glyphs_for_row(&row, &options, &mut stats)
|
|
|
|
.expect("first row")
|
|
|
|
.expect("first row")
|
|
|
|
|
|
|
|
.1
|
|
|
|
.to_vec();
|
|
|
|
.to_vec();
|
|
|
|
assert_eq!(cache.glyph_rows.len(), 1);
|
|
|
|
assert_eq!(cache.glyph_rows.len(), 1);
|
|
|
|
let second = cache
|
|
|
|
let second = cache
|
|
|
|
.glyphs_for_row(&row, &options, &mut stats)
|
|
|
|
.glyphs_for_row(&row, &options, &mut stats)
|
|
|
|
.expect("cached row")
|
|
|
|
.expect("cached row")
|
|
|
|
|
|
|
|
.1
|
|
|
|
.to_vec();
|
|
|
|
.to_vec();
|
|
|
|
assert_eq!(cache.glyph_rows.len(), 1);
|
|
|
|
assert_eq!(cache.glyph_rows.len(), 1);
|
|
|
|
assert_eq!(first, second);
|
|
|
|
assert_eq!(first, second);
|
|
|
|
@@ -1782,6 +1898,7 @@ mod tests {
|
|
|
|
let changed = cache
|
|
|
|
let changed = cache
|
|
|
|
.glyphs_for_row(&row, &options, &mut stats)
|
|
|
|
.glyphs_for_row(&row, &options, &mut stats)
|
|
|
|
.expect("theme changed row")
|
|
|
|
.expect("theme changed row")
|
|
|
|
|
|
|
|
.1
|
|
|
|
.to_vec();
|
|
|
|
.to_vec();
|
|
|
|
assert_eq!(cache.glyph_rows.len(), 2);
|
|
|
|
assert_eq!(cache.glyph_rows.len(), 2);
|
|
|
|
assert_ne!(first[0].fg, changed[0].fg);
|
|
|
|
assert_ne!(first[0].fg, changed[0].fg);
|
|
|
|
@@ -1803,15 +1920,59 @@ mod tests {
|
|
|
|
let mut cache = WebGlRowCache::default();
|
|
|
|
let mut cache = WebGlRowCache::default();
|
|
|
|
let rows = vec![row.clone(), row];
|
|
|
|
let rows = vec![row.clone(), row];
|
|
|
|
|
|
|
|
|
|
|
|
let (cells, stats) = collect_glyph_cells(&rows, &options, &mut cache).expect("cells");
|
|
|
|
let (glyph_rows, cell_count, stats) =
|
|
|
|
|
|
|
|
collect_glyph_rows(&rows, &options, &mut cache).expect("cells");
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(cells.len(), 4);
|
|
|
|
assert_eq!(glyph_rows.len(), 2);
|
|
|
|
assert_eq!(cells[0].row, 0);
|
|
|
|
assert_eq!(cell_count, 4);
|
|
|
|
assert_eq!(cells[1].row, 0);
|
|
|
|
assert_eq!(glyph_rows[0].row, 0);
|
|
|
|
assert_eq!(cells[2].row, 1);
|
|
|
|
assert_eq!(glyph_rows[0].cell_count, 2);
|
|
|
|
assert_eq!(cells[3].row, 1);
|
|
|
|
assert_eq!(glyph_rows[1].row, 1);
|
|
|
|
|
|
|
|
assert_eq!(glyph_rows[1].cell_count, 2);
|
|
|
|
assert_eq!(cache.glyph_rows.len(), 1);
|
|
|
|
assert_eq!(cache.glyph_rows.len(), 1);
|
|
|
|
assert_eq!(stats, WebGlRowCacheStats { hits: 1, misses: 1 });
|
|
|
|
assert_eq!(
|
|
|
|
|
|
|
|
stats,
|
|
|
|
|
|
|
|
WebGlRowCacheStats {
|
|
|
|
|
|
|
|
hits: 1,
|
|
|
|
|
|
|
|
misses: 1,
|
|
|
|
|
|
|
|
vertex_hits: 0,
|
|
|
|
|
|
|
|
vertex_misses: 0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
fn row_vertex_template_offsets_y_without_touching_x_or_uv() {
|
|
|
|
|
|
|
|
let dom = CanvasDomState {
|
|
|
|
|
|
|
|
backing_width: 200,
|
|
|
|
|
|
|
|
backing_height: 100,
|
|
|
|
|
|
|
|
css_width: 100,
|
|
|
|
|
|
|
|
css_height: 50,
|
|
|
|
|
|
|
|
top_px: 0.0,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut template = Vec::new();
|
|
|
|
|
|
|
|
push_quad(
|
|
|
|
|
|
|
|
&mut template,
|
|
|
|
|
|
|
|
GlyphQuad {
|
|
|
|
|
|
|
|
x: 10.0,
|
|
|
|
|
|
|
|
y: 0.0,
|
|
|
|
|
|
|
|
width: 10.0,
|
|
|
|
|
|
|
|
height: 10.0,
|
|
|
|
|
|
|
|
uv: [0.1, 0.2, 0.3, 0.4],
|
|
|
|
|
|
|
|
color: [1.0, 0.5, 0.25, 1.0],
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
dom,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut vertices = Vec::new();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
append_row_vertex_template(&mut vertices, &template, 5.0, dom);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(vertices.len(), template.len());
|
|
|
|
|
|
|
|
assert_eq!(vertices[0], template[0]);
|
|
|
|
|
|
|
|
assert_eq!(vertices[2], template[2]);
|
|
|
|
|
|
|
|
assert_eq!(vertices[4], template[4]);
|
|
|
|
|
|
|
|
assert_eq!(vertices[1], template[1] - 0.2);
|
|
|
|
|
|
|
|
assert_eq!(vertices[9], template[9] - 0.2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[test]
|
|
|
|
|