forked from NotAShelf/beer
render: cut per-frame cost with a fast clear and row fills
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I96cdacecbe2a55c42825006e84fede076a6a6964
This commit is contained in:
parent
b2d656e7bd
commit
7254cbf381
2 changed files with 33 additions and 14 deletions
|
|
@ -12,7 +12,7 @@ use crate::grid::{Cell, Color, CursorShape, Flags, Grid, Underline};
|
||||||
const DEFAULT_FG: Rgb = Rgb(0xc5, 0xc8, 0xc6);
|
const DEFAULT_FG: Rgb = Rgb(0xc5, 0xc8, 0xc6);
|
||||||
const DEFAULT_BG: Rgb = Rgb(0x18, 0x18, 0x18);
|
const DEFAULT_BG: Rgb = Rgb(0x18, 0x18, 0x18);
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
struct Rgb(u8, u8, u8);
|
struct Rgb(u8, u8, u8);
|
||||||
|
|
||||||
/// A mutable view over a BGRA pixel buffer.
|
/// A mutable view over a BGRA pixel buffer.
|
||||||
|
|
@ -30,16 +30,30 @@ impl Canvas<'_> {
|
||||||
Some((y as usize * self.width + x as usize) * 4)
|
Some((y as usize * self.width + x as usize) * 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_rect(&mut self, x0: i32, y0: i32, w: u32, h: u32, c: Rgb) {
|
/// Fill the whole buffer with one colour (fast path, no per-pixel bounds
|
||||||
for dy in 0..h as i32 {
|
/// checks).
|
||||||
for dx in 0..w as i32 {
|
fn clear(&mut self, c: Rgb) {
|
||||||
if let Some(i) = self.index(x0 + dx, y0 + dy) {
|
let bytes = [c.2, c.1, c.0, 0xff];
|
||||||
self.pixels[i] = c.2;
|
for px in self.pixels.chunks_exact_mut(4) {
|
||||||
self.pixels[i + 1] = c.1;
|
px.copy_from_slice(&bytes);
|
||||||
self.pixels[i + 2] = c.0;
|
|
||||||
self.pixels[i + 3] = 0xff;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fill_rect(&mut self, x0: i32, y0: i32, w: u32, h: u32, c: Rgb) {
|
||||||
|
let x_start = x0.max(0) as usize;
|
||||||
|
let x_end = ((x0 + w as i32).max(0) as usize).min(self.width);
|
||||||
|
let y_start = y0.max(0) as usize;
|
||||||
|
let y_end = ((y0 + h as i32).max(0) as usize).min(self.height);
|
||||||
|
if x_start >= x_end {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let bytes = [c.2, c.1, c.0, 0xff];
|
||||||
|
for y in y_start..y_end {
|
||||||
|
let row =
|
||||||
|
&mut self.pixels[(y * self.width + x_start) * 4..(y * self.width + x_end) * 4];
|
||||||
|
for px in row.chunks_exact_mut(4) {
|
||||||
|
px.copy_from_slice(&bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,17 +123,21 @@ impl Renderer {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
};
|
};
|
||||||
canvas.fill_rect(0, 0, width as u32, height as u32, DEFAULT_BG);
|
canvas.clear(DEFAULT_BG);
|
||||||
|
|
||||||
let m = self.fonts.metrics();
|
let m = self.fonts.metrics();
|
||||||
|
|
||||||
|
// Cell backgrounds: only paint cells that differ from the cleared
|
||||||
|
// default - most of a screen is default background.
|
||||||
for y in 0..grid.rows() {
|
for y in 0..grid.rows() {
|
||||||
for x in 0..grid.cols() {
|
for x in 0..grid.cols() {
|
||||||
let (_, bg) = cell_colors(grid.cell(x, y));
|
let (_, bg) = cell_colors(grid.cell(x, y));
|
||||||
|
if bg != DEFAULT_BG {
|
||||||
let (px, py) = (x as i32 * m.width as i32, y as i32 * m.height as i32);
|
let (px, py) = (x as i32 * m.width as i32, y as i32 * m.height as i32);
|
||||||
canvas.fill_rect(px, py, m.width, m.height, bg);
|
canvas.fill_rect(px, py, m.width, m.height, bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for y in 0..grid.rows() {
|
for y in 0..grid.rows() {
|
||||||
for x in 0..grid.cols() {
|
for x in 0..grid.cols() {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
use std::os::fd::OwnedFd;
|
use std::os::fd::OwnedFd;
|
||||||
use std::os::unix::process::ExitStatusExt;
|
use std::os::unix::process::ExitStatusExt;
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use calloop::generic::Generic;
|
use calloop::generic::Generic;
|
||||||
|
|
@ -105,9 +104,11 @@ pub fn run() -> anyhow::Result<ExitCode> {
|
||||||
exit_code: ExitCode::SUCCESS,
|
exit_code: ExitCode::SUCCESS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Block until an event (PTY output, input, configure) arrives - every
|
||||||
|
// repaint is driven by one, so there is nothing to do on a timer.
|
||||||
while !app.exit {
|
while !app.exit {
|
||||||
event_loop
|
event_loop
|
||||||
.dispatch(Duration::from_millis(16), &mut app)
|
.dispatch(None, &mut app)
|
||||||
.context("dispatch event loop")?;
|
.context("dispatch event loop")?;
|
||||||
if app.dirty {
|
if app.dirty {
|
||||||
app.draw();
|
app.draw();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue