use super::{Cell, CellRenderer, PaneViewport};
use anyhow::Result;
use par_term_config::SeparatorMark;
mod block_char_render;
mod cursor_overlays;
mod powerline;
mod render_to_view;
mod row_backgrounds;
mod separators;
mod text_render;
use cursor_overlays::CursorOverlayParams;
use row_backgrounds::RowBackgroundParams;
use text_render::RowTextParams;
pub(crate) const ATLAS_SIZE: f32 = 2048.0;
pub struct PaneRenderViewParams<'a> {
pub viewport: &'a PaneViewport,
pub cells: &'a [Cell],
pub cols: usize,
pub rows: usize,
pub cursor_pos: Option<(usize, usize)>,
pub cursor_opacity: f32,
pub show_scrollbar: bool,
pub clear_first: bool,
pub skip_background_image: bool,
pub fill_default_bg_cells: bool,
pub separator_marks: &'a [SeparatorMark],
pub pane_background: Option<&'a par_term_config::PaneBackground>,
}
pub(super) struct PaneInstanceBuildParams<'a> {
pub viewport: &'a PaneViewport,
pub cells: &'a [Cell],
pub cols: usize,
pub rows: usize,
pub cursor_pos: Option<(usize, usize)>,
pub cursor_opacity: f32,
pub skip_solid_background: bool,
pub fill_default_bg_cells: bool,
pub separator_marks: &'a [SeparatorMark],
}
pub(super) struct PaneInstanceRanges {
pub bg: std::ops::Range<usize>,
pub cursor_overlays: std::ops::Range<usize>,
pub text: std::ops::Range<usize>,
}
pub(crate) fn pane_instance_capacity(cols: usize, rows: usize) -> (usize, usize) {
let cells = cols * rows;
(
1 + cells + rows + super::CURSOR_OVERLAY_SLOTS,
cells * super::instance_buffers::PANE_TEXT_INSTANCES_PER_CELL,
)
}
impl CellRenderer {
pub(crate) fn begin_pane_batch(&mut self, required_bg: usize, required_text: usize) {
let (grid_bg, grid_text) = self.single_grid_instance_capacity();
let need_bg = required_bg.max(grid_bg);
let need_text = required_text.max(grid_text);
if need_bg > self.buffers.max_bg_instances || need_text > self.buffers.max_text_instances {
log::info!(
"Growing instance buffers for {} bg / {} text instances (was {} / {})",
need_bg,
need_text,
self.buffers.max_bg_instances,
self.buffers.max_text_instances,
);
self.allocate_instance_buffers(
need_bg.max(self.buffers.max_bg_instances),
need_text.max(self.buffers.max_text_instances),
);
}
self.buffers.pane_bg_cursor = 0;
self.buffers.pane_text_cursor = 0;
}
fn build_pane_instance_buffers(
&mut self,
p: PaneInstanceBuildParams<'_>,
) -> Result<PaneInstanceRanges> {
let PaneInstanceBuildParams {
viewport,
cells,
cols,
rows,
cursor_pos,
cursor_opacity,
skip_solid_background,
fill_default_bg_cells,
separator_marks,
} = p;
let bg_base = self.buffers.pane_bg_cursor;
let text_base = self.buffers.pane_text_cursor;
let bg_start_index = if !skip_solid_background && bg_base < self.bg_instances.len() {
let bg_color = self.background_color;
let opacity = self.window_opacity * viewport.opacity;
let width_f = self.config.width as f32;
let height_f = self.config.height as f32;
self.bg_instances[bg_base] = super::types::BackgroundInstance {
position: [
viewport.x / width_f * 2.0 - 1.0,
1.0 - (viewport.y / height_f * 2.0),
],
size: [
viewport.width / width_f * 2.0,
viewport.height / height_f * 2.0,
],
color: [
bg_color[0] * opacity,
bg_color[1] * opacity,
bg_color[2] * opacity,
opacity,
],
};
bg_base + 1 } else {
bg_base };
let mut bg_index = bg_start_index;
let mut text_index = text_base;
let (content_x, content_y) = viewport.content_origin();
let opacity_multiplier = viewport.opacity;
for row in 0..rows {
let row_start = row * cols;
let row_end = (row + 1) * cols;
if row_start >= cells.len() {
break;
}
let row_cells = &cells[row_start..row_end.min(cells.len())];
bg_index = self.emit_row_backgrounds(
RowBackgroundParams {
row_cells,
row,
cursor_pos,
cursor_opacity,
content_x,
content_y,
opacity_multiplier,
fill_default_bg_cells,
skip_solid_background,
},
bg_index,
);
text_index = self.emit_row_text(
RowTextParams {
row_cells,
cells,
row_start,
cols,
row,
content_x,
content_y,
cursor_pos,
cursor_opacity,
opacity_multiplier,
},
text_index,
);
}
bg_index = self.emit_separator_instances(
separator_marks,
cols,
rows,
content_x,
content_y,
opacity_multiplier,
bg_index,
);
let cursor_overlay_start = bg_index;
if let Some((cursor_col, cursor_row)) = cursor_pos {
let cursor_x0 = content_x + cursor_col as f32 * self.grid.cell_width;
let cursor_x1 = cursor_x0 + self.grid.cell_width;
let cursor_y0 = (content_y + cursor_row as f32 * self.grid.cell_height).round();
let cursor_y1 = (content_y + (cursor_row + 1) as f32 * self.grid.cell_height).round();
bg_index = self.emit_cursor_overlays(
CursorOverlayParams {
cursor_x0,
cursor_x1,
cursor_y0,
cursor_y1,
cols,
content_x,
cursor_opacity,
},
bg_index,
);
}
self.buffers.pane_bg_cursor = bg_index;
self.buffers.pane_text_cursor = text_index;
if (bg_index >= self.buffers.max_bg_instances
|| text_index >= self.buffers.max_text_instances)
&& !self.buffers.overflow_reported
{
self.buffers.overflow_reported = true;
log::error!(
"Instance buffers exhausted while building a pane batch: bg {bg_index}/{} text {text_index}/{}. Later panes in this frame will be missing content.",
self.buffers.max_bg_instances,
self.buffers.max_text_instances,
);
}
self.dirty_rows.fill(true);
if bg_index > bg_base {
self.queue.write_buffer(
&self.buffers.bg_instance_buffer,
(bg_base * std::mem::size_of::<super::types::BackgroundInstance>()) as u64,
bytemuck::cast_slice(&self.bg_instances[bg_base..bg_index]),
);
}
if text_index > text_base {
self.queue.write_buffer(
&self.buffers.text_instance_buffer,
(text_base * std::mem::size_of::<super::types::TextInstance>()) as u64,
bytemuck::cast_slice(&self.text_instances[text_base..text_index]),
);
}
Ok(PaneInstanceRanges {
bg: bg_base..cursor_overlay_start,
cursor_overlays: cursor_overlay_start..bg_index,
text: text_base..text_index,
})
}
}