forked from Bananymous/banan-os
Terminal: Optimize printing a lot
Terminal used to run `yes` at around 400 lines per second This patch pumps that to over 100'000 lines per second! There are 2 optimizations done: 1. only invalidate window once after rendering is done 2. if printing more than `rows()` newlines skip prior data
This commit is contained in:
parent
7feb4c4ebd
commit
c3040a04a3
|
@ -196,14 +196,69 @@ void Terminal::show_cursor()
|
||||||
|
|
||||||
bool Terminal::read_shell()
|
bool Terminal::read_shell()
|
||||||
{
|
{
|
||||||
char buffer[128];
|
char buffer[512];
|
||||||
ssize_t nread = read(m_shell_info.pts_master, buffer, sizeof(buffer) - 1);
|
ssize_t nread = read(m_shell_info.pts_master, buffer, sizeof(buffer));
|
||||||
if (nread < 0)
|
if (nread < 0)
|
||||||
dwarnln("read: {}", strerror(errno));
|
dwarnln("read: {}", strerror(errno));
|
||||||
if (nread <= 0)
|
if (nread <= 0)
|
||||||
return false;
|
return false;
|
||||||
for (ssize_t i = 0; i < nread; i++)
|
|
||||||
putchar(buffer[i]);
|
Rectangle should_invalidate;
|
||||||
|
|
||||||
|
ssize_t i = 0;
|
||||||
|
while (i < nread)
|
||||||
|
{
|
||||||
|
// all ansi escape codes must be handled
|
||||||
|
if (buffer[i] == '\e')
|
||||||
|
{
|
||||||
|
while (i < nread)
|
||||||
|
{
|
||||||
|
char ch = buffer[i++];
|
||||||
|
should_invalidate = should_invalidate.get_bounding_box(putchar(ch));
|
||||||
|
if (isalpha(ch))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the next ansi escape code or end of buffer
|
||||||
|
size_t non_ansi_end = i;
|
||||||
|
while (non_ansi_end < nread && buffer[non_ansi_end] != '\e')
|
||||||
|
non_ansi_end++;
|
||||||
|
|
||||||
|
// we only need to process maximum of `rows()` newlines.
|
||||||
|
// anything before that would get overwritten anyway
|
||||||
|
size_t start = non_ansi_end;
|
||||||
|
size_t newline_count = 0;
|
||||||
|
while (start > i && newline_count < rows())
|
||||||
|
newline_count += (buffer[--start] == '\n');
|
||||||
|
|
||||||
|
// do possible scrolling already in here, so `putchar()` doesnt
|
||||||
|
// have to scroll up to `rows()` times
|
||||||
|
if (m_cursor.y + newline_count >= rows())
|
||||||
|
{
|
||||||
|
const uint32_t scroll = m_cursor.y + newline_count - rows() + 1;
|
||||||
|
m_cursor.y -= scroll;
|
||||||
|
m_window->shift_vertical(-scroll * (int32_t)m_font.height());
|
||||||
|
m_window->fill_rect(0, m_window->height() - scroll * m_font.height(), m_window->width(), scroll * m_font.height(), m_bg_color);
|
||||||
|
should_invalidate = { 0, 0, m_window->width(), m_window->height() };
|
||||||
|
}
|
||||||
|
|
||||||
|
i = start;
|
||||||
|
for (i = start; i < non_ansi_end; i++)
|
||||||
|
should_invalidate = should_invalidate.get_bounding_box(putchar(buffer[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_invalidate.height && should_invalidate.width)
|
||||||
|
{
|
||||||
|
m_window->invalidate(
|
||||||
|
should_invalidate.x,
|
||||||
|
should_invalidate.y,
|
||||||
|
should_invalidate.width,
|
||||||
|
should_invalidate.height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,7 +416,7 @@ void Terminal::handle_csi(char ch)
|
||||||
m_state = State::Normal;
|
m_state = State::Normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::putchar(uint8_t ch)
|
Rectangle Terminal::putchar(uint8_t ch)
|
||||||
{
|
{
|
||||||
if (m_state == State::ESC)
|
if (m_state == State::ESC)
|
||||||
{
|
{
|
||||||
|
@ -369,14 +424,14 @@ void Terminal::putchar(uint8_t ch)
|
||||||
{
|
{
|
||||||
dprintln("unknown escape character 0x{2H}", ch);
|
dprintln("unknown escape character 0x{2H}", ch);
|
||||||
m_state = State::Normal;
|
m_state = State::Normal;
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
m_state = State::CSI;
|
m_state = State::CSI;
|
||||||
m_csi_info.index = 0;
|
m_csi_info.index = 0;
|
||||||
m_csi_info.fields[0] = -1;
|
m_csi_info.fields[0] = -1;
|
||||||
m_csi_info.fields[1] = -1;
|
m_csi_info.fields[1] = -1;
|
||||||
m_csi_info.question = false;
|
m_csi_info.question = false;
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_state == State::CSI)
|
if (m_state == State::CSI)
|
||||||
|
@ -385,10 +440,10 @@ void Terminal::putchar(uint8_t ch)
|
||||||
{
|
{
|
||||||
dprintln("invalid CSI 0x{2H}", ch);
|
dprintln("invalid CSI 0x{2H}", ch);
|
||||||
m_state = State::Normal;
|
m_state = State::Normal;
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
handle_csi(ch);
|
handle_csi(ch);
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
m_utf8_bytes[m_utf8_index++] = ch;
|
m_utf8_bytes[m_utf8_index++] = ch;
|
||||||
|
@ -398,10 +453,10 @@ void Terminal::putchar(uint8_t ch)
|
||||||
{
|
{
|
||||||
dwarnln("invalid utf8 leading byte 0x{2H}", ch);
|
dwarnln("invalid utf8 leading byte 0x{2H}", ch);
|
||||||
m_utf8_index = 0;
|
m_utf8_index = 0;
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
if (m_utf8_index < utf8_len)
|
if (m_utf8_index < utf8_len)
|
||||||
return;
|
return {};
|
||||||
|
|
||||||
const uint32_t codepoint = BAN::UTF8::to_codepoint(m_utf8_bytes);
|
const uint32_t codepoint = BAN::UTF8::to_codepoint(m_utf8_bytes);
|
||||||
m_utf8_index = 0;
|
m_utf8_index = 0;
|
||||||
|
@ -421,9 +476,11 @@ void Terminal::putchar(uint8_t ch)
|
||||||
*--ptr = '\0';
|
*--ptr = '\0';
|
||||||
|
|
||||||
dwarnln("invalid utf8 {}", utf8_hex);
|
dwarnln("invalid utf8 {}", utf8_hex);
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle should_invalidate;
|
||||||
|
|
||||||
switch (codepoint)
|
switch (codepoint)
|
||||||
{
|
{
|
||||||
case '\e':
|
case '\e':
|
||||||
|
@ -449,7 +506,7 @@ void Terminal::putchar(uint8_t ch)
|
||||||
|
|
||||||
m_window->fill_rect(cell_x, cell_y, cell_w, cell_h, m_bg_color);
|
m_window->fill_rect(cell_x, cell_y, cell_w, cell_h, m_bg_color);
|
||||||
m_window->draw_character(codepoint, m_font, cell_x, cell_y, m_fg_color);
|
m_window->draw_character(codepoint, m_font, cell_x, cell_y, m_fg_color);
|
||||||
m_window->invalidate(cell_x, cell_y, cell_w, cell_h);
|
should_invalidate = { cell_x, cell_y, cell_w, cell_h };
|
||||||
m_cursor.x++;
|
m_cursor.x++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -461,14 +518,10 @@ void Terminal::putchar(uint8_t ch)
|
||||||
m_cursor.y++;
|
m_cursor.y++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cursor.y >= rows())
|
// scrolling is already handled in `read_shell()`
|
||||||
{
|
ASSERT(m_cursor.y < rows());
|
||||||
uint32_t scroll = m_cursor.y - rows() + 1;
|
|
||||||
m_cursor.y -= scroll;
|
return should_invalidate;
|
||||||
m_window->shift_vertical(-scroll * (int32_t)m_font.height());
|
|
||||||
m_window->fill_rect(0, m_window->height() - scroll * m_font.height(), m_window->width(), scroll * m_font.height(), m_bg_color);
|
|
||||||
m_window->invalidate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::on_key_event(LibGUI::EventPacket::KeyEvent event)
|
void Terminal::on_key_event(LibGUI::EventPacket::KeyEvent event)
|
||||||
|
|
|
@ -4,6 +4,28 @@
|
||||||
|
|
||||||
#include <LibGUI/Window.h>
|
#include <LibGUI/Window.h>
|
||||||
|
|
||||||
|
struct Rectangle
|
||||||
|
{
|
||||||
|
uint32_t x { 0 };
|
||||||
|
uint32_t y { 0 };
|
||||||
|
uint32_t width { 0 };
|
||||||
|
uint32_t height { 0 };
|
||||||
|
|
||||||
|
Rectangle get_bounding_box(Rectangle other) const
|
||||||
|
{
|
||||||
|
const auto min_x = BAN::Math::min(x, other.x);
|
||||||
|
const auto min_y = BAN::Math::min(y, other.y);
|
||||||
|
const auto max_x = BAN::Math::max(x + width, other.x + other.width);
|
||||||
|
const auto max_y = BAN::Math::max(y + height, other.y + other.height);
|
||||||
|
return Rectangle {
|
||||||
|
.x = min_x,
|
||||||
|
.y = min_y,
|
||||||
|
.width = max_x - min_x,
|
||||||
|
.height = max_y - min_y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class Terminal
|
class Terminal
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -15,7 +37,7 @@ public:
|
||||||
private:
|
private:
|
||||||
void handle_csi(char ch);
|
void handle_csi(char ch);
|
||||||
void handle_sgr();
|
void handle_sgr();
|
||||||
void putchar(uint8_t ch);
|
Rectangle putchar(uint8_t ch);
|
||||||
bool read_shell();
|
bool read_shell();
|
||||||
|
|
||||||
void hide_cursor();
|
void hide_cursor();
|
||||||
|
|
Loading…
Reference in New Issue