mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 08:02:44 +00:00 
			
		
		
		
	Terminal: Redraw entire line if any of its characters are dirty.
This means we only have to do one fill_rect() per line and the whole process ends up being ~10% faster than before. Also added a read_tsc() syscall to give userspace access to the TSC.
This commit is contained in:
		
							parent
							
								
									11b73c38d8
								
							
						
					
					
						commit
						267a903dd0
					
				
					 9 changed files with 108 additions and 35 deletions
				
			
		|  | @ -2081,3 +2081,13 @@ int Process::sys$unlink(const char* pathname) | ||||||
|         return error; |         return error; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | int Process::sys$read_tsc(dword* lsw, dword* msw) | ||||||
|  | { | ||||||
|  |     if (!validate_write_typed(lsw)) | ||||||
|  |         return -EFAULT; | ||||||
|  |     if (!validate_write_typed(msw)) | ||||||
|  |         return -EFAULT; | ||||||
|  |     read_tsc(*lsw, *msw); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -194,6 +194,7 @@ public: | ||||||
|     clock_t sys$times(tms*); |     clock_t sys$times(tms*); | ||||||
|     int sys$utime(const char* pathname, const struct utimbuf*); |     int sys$utime(const char* pathname, const struct utimbuf*); | ||||||
|     int sys$unlink(const char* pathname); |     int sys$unlink(const char* pathname); | ||||||
|  |     int sys$read_tsc(dword* lsw, dword* msw); | ||||||
| 
 | 
 | ||||||
|     int gui$create_window(const GUI_WindowParameters*); |     int gui$create_window(const GUI_WindowParameters*); | ||||||
|     int gui$destroy_window(int window_id); |     int gui$destroy_window(int window_id); | ||||||
|  |  | ||||||
|  | @ -213,6 +213,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, | ||||||
|         return current->gui$set_window_rect((int)arg1, (const GUI_Rect*)arg2); |         return current->gui$set_window_rect((int)arg1, (const GUI_Rect*)arg2); | ||||||
|     case Syscall::SC_gui_get_window_rect: |     case Syscall::SC_gui_get_window_rect: | ||||||
|         return current->gui$get_window_rect((int)arg1, (GUI_Rect*)arg2); |         return current->gui$get_window_rect((int)arg1, (GUI_Rect*)arg2); | ||||||
|  |     case Syscall::SC_read_tsc: | ||||||
|  |         return current->sys$read_tsc((dword*)arg1, (dword*)arg2); | ||||||
|     default: |     default: | ||||||
|         kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); |         kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|  | @ -70,6 +70,7 @@ | ||||||
|     __ENUMERATE_SYSCALL(select) \ |     __ENUMERATE_SYSCALL(select) \ | ||||||
|     __ENUMERATE_SYSCALL(unlink) \ |     __ENUMERATE_SYSCALL(unlink) \ | ||||||
|     __ENUMERATE_SYSCALL(poll) \ |     __ENUMERATE_SYSCALL(poll) \ | ||||||
|  |     __ENUMERATE_SYSCALL(read_tsc) \ | ||||||
|     __ENUMERATE_SYSCALL(gui_create_window) \ |     __ENUMERATE_SYSCALL(gui_create_window) \ | ||||||
|     __ENUMERATE_SYSCALL(gui_destroy_window) \ |     __ENUMERATE_SYSCALL(gui_destroy_window) \ | ||||||
|     __ENUMERATE_SYSCALL(gui_get_window_backing_store) \ |     __ENUMERATE_SYSCALL(gui_get_window_backing_store) \ | ||||||
|  |  | ||||||
|  | @ -306,4 +306,10 @@ void sync() | ||||||
|     syscall(SC_sync); |     syscall(SC_sync); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int read_tsc(unsigned* lsw, unsigned* msw) | ||||||
|  | { | ||||||
|  |     int rc = syscall(SC_read_tsc, lsw, msw); | ||||||
|  |     __RETURN_WITH_ERRNO(rc, rc, -1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ __BEGIN_DECLS | ||||||
| 
 | 
 | ||||||
| extern char** environ; | extern char** environ; | ||||||
| 
 | 
 | ||||||
|  | int read_tsc(unsigned* lsw, unsigned* msw); | ||||||
| inline int getpagesize() { return 4096; } | inline int getpagesize() { return 4096; } | ||||||
| pid_t fork(); | pid_t fork(); | ||||||
| int execve(const char* filename, char* const argv[], char* const envp[]); | int execve(const char* filename, char* const argv[], char* const envp[]); | ||||||
|  |  | ||||||
|  | @ -4,6 +4,11 @@ | ||||||
| 
 | 
 | ||||||
| typedef dword RGBA32; | typedef dword RGBA32; | ||||||
| 
 | 
 | ||||||
|  | inline constexpr dword make_rgb(byte r, byte g, byte b) | ||||||
|  | { | ||||||
|  |     return ((r << 16) | (g << 8) | b); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| class Color { | class Color { | ||||||
| public: | public: | ||||||
|     enum NamedColor { |     enum NamedColor { | ||||||
|  |  | ||||||
|  | @ -10,6 +10,33 @@ | ||||||
| 
 | 
 | ||||||
| //#define TERMINAL_DEBUG
 | //#define TERMINAL_DEBUG
 | ||||||
| 
 | 
 | ||||||
|  | struct Stopwatch { | ||||||
|  | public: | ||||||
|  |     Stopwatch(const char* name) | ||||||
|  |         : m_name(name) | ||||||
|  |     { | ||||||
|  |         read_tsc(&m_start_lsw, &m_start_msw); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ~Stopwatch() | ||||||
|  |     { | ||||||
|  |         dword end_lsw; | ||||||
|  |         dword end_msw; | ||||||
|  |         read_tsc(&end_lsw, &end_msw); | ||||||
|  |         if (m_start_msw != end_msw) { | ||||||
|  |             dbgprintf("stopwatch: differing msw, no result for %s\n", m_name); | ||||||
|  |         } | ||||||
|  |         dword diff = end_lsw - m_start_lsw; | ||||||
|  |         dbgprintf("Stopwatch(%s): %u ticks\n", m_name, diff); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     const char* m_name { nullptr }; | ||||||
|  |     dword m_start_lsw { 0 }; | ||||||
|  |     dword m_start_msw { 0 }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| void Terminal::create_window() | void Terminal::create_window() | ||||||
| { | { | ||||||
|     m_pixel_width = m_columns * font().glyph_width() + m_inset * 2; |     m_pixel_width = m_columns * font().glyph_width() + m_inset * 2; | ||||||
|  | @ -73,6 +100,7 @@ Terminal::Line::~Line() | ||||||
| 
 | 
 | ||||||
| void Terminal::Line::clear() | void Terminal::Line::clear() | ||||||
| { | { | ||||||
|  |     dirty = true; | ||||||
|     memset(characters, ' ', length); |     memset(characters, ' ', length); | ||||||
|     for (word i = 0 ; i < length; ++i) |     for (word i = 0 ; i < length; ++i) | ||||||
|         attributes[i].reset(); |         attributes[i].reset(); | ||||||
|  | @ -144,26 +172,25 @@ enum ANSIColor : byte { | ||||||
| 
 | 
 | ||||||
| static inline Color ansi_color(unsigned color) | static inline Color ansi_color(unsigned color) | ||||||
| { | { | ||||||
|     switch (color) { |     static const RGBA32 s_ansi_color[16] = { | ||||||
|     case ANSIColor::Black: return Color(0, 0, 0); |         make_rgb(0, 0, 0),          // Black
 | ||||||
|     case ANSIColor::Red: return Color(225, 56, 43); |         make_rgb(225, 56, 43),      // Red
 | ||||||
|     case ANSIColor::Green: return Color(57, 181, 74); |         make_rgb(57, 181, 74),      // Green
 | ||||||
|     case ANSIColor::Brown: return Color(255, 199, 6); |         make_rgb(255, 199, 6),      // Brown
 | ||||||
|     case ANSIColor::Blue: return Color(0, 111, 184); |         make_rgb(0, 11, 184),       // Blue
 | ||||||
|     case ANSIColor::Magenta: return Color(118, 38, 113); |         make_rgb(118, 38, 113),     // Magenta
 | ||||||
|     case ANSIColor::Cyan: return Color(44, 181, 233); |         make_rgb(44, 181, 233),     // Cyan
 | ||||||
|     case ANSIColor::LightGray: return Color(204, 204, 204); |         make_rgb(204, 204, 204),    // LightGray
 | ||||||
|     case ANSIColor::DarkGray: return Color(128, 128, 128); |         make_rgb(128, 128, 128),    // DarkGray
 | ||||||
|     case ANSIColor::BrightRed: return Color(255, 0, 0); |         make_rgb(255, 0, 0),        // BrightRed
 | ||||||
|     case ANSIColor::BrightGreen: return Color(0, 255, 0); |         make_rgb(0, 255, 0),        // BrightGreen
 | ||||||
|     case ANSIColor::Yellow: return Color(255, 255, 0); |         make_rgb(255, 255, 0),      // Yellow
 | ||||||
|     case ANSIColor::BrightBlue: return Color(0, 0, 255); |         make_rgb(0, 0, 255),        // BrightBlue
 | ||||||
|     case ANSIColor::BrightMagenta: return Color(255, 0, 255); |         make_rgb(255, 0, 255),      // BrightMagenta
 | ||||||
|     case ANSIColor::BrightCyan: return Color(0, 255, 255); |         make_rgb(0, 255, 255),      // BrightCyan
 | ||||||
|     case ANSIColor::White: return Color(255, 255, 255); |         make_rgb(255, 255, 255),    // White
 | ||||||
|     } |     }; | ||||||
|     ASSERT_NOT_REACHED(); |     return s_ansi_color[color]; | ||||||
|     return Color::White; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Terminal::escape$m(const Vector<unsigned>& params) | void Terminal::escape$m(const Vector<unsigned>& params) | ||||||
|  | @ -176,7 +203,7 @@ void Terminal::escape$m(const Vector<unsigned>& params) | ||||||
|             break; |             break; | ||||||
|         case 1: |         case 1: | ||||||
|             // Bold
 |             // Bold
 | ||||||
|             m_current_attribute.bold = true; |             //m_current_attribute.bold = true;
 | ||||||
|             break; |             break; | ||||||
|         case 30: |         case 30: | ||||||
|         case 31: |         case 31: | ||||||
|  | @ -516,8 +543,22 @@ inline Terminal::Attribute& Terminal::attribute_at(word row, word column) | ||||||
|     return line(row).attributes[column]; |     return line(row).attributes[column]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool Terminal::Line::has_only_one_background_color() const | ||||||
|  | { | ||||||
|  |     if (!length) | ||||||
|  |         return true; | ||||||
|  |     // FIXME: Cache this result?
 | ||||||
|  |     auto color = attributes[0].background_color; | ||||||
|  |     for (size_t i = 1; i < length; ++i) { | ||||||
|  |         if (attributes[i].background_color != color) | ||||||
|  |             return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Terminal::paint() | void Terminal::paint() | ||||||
| { | { | ||||||
|  |     Stopwatch sw("Terminal::paint"); | ||||||
|     Rect rect { 0, 0, m_pixel_width, m_pixel_height }; |     Rect rect { 0, 0, m_pixel_width, m_pixel_height }; | ||||||
|     Painter painter(*m_backing); |     Painter painter(*m_backing); | ||||||
| 
 | 
 | ||||||
|  | @ -535,21 +576,27 @@ void Terminal::paint() | ||||||
|             scanlines_to_copy * m_pixel_width |             scanlines_to_copy * m_pixel_width | ||||||
|         ); |         ); | ||||||
|         m_need_full_invalidation = true; |         m_need_full_invalidation = true; | ||||||
|         attribute_at(max(0, m_cursor_row - m_rows_to_scroll_backing_store), m_cursor_column).dirty = true; |         line(max(0, m_cursor_row - m_rows_to_scroll_backing_store)).dirty = true; | ||||||
|     } |     } | ||||||
|     m_rows_to_scroll_backing_store = 0; |     m_rows_to_scroll_backing_store = 0; | ||||||
| 
 | 
 | ||||||
|     for (word row = 0; row < m_rows; ++row) { |     for (word row = 0; row < m_rows; ++row) { | ||||||
|  |         auto& line = this->line(row); | ||||||
|  |         if (!line.dirty) | ||||||
|  |             continue; | ||||||
|  |         line.dirty = false; | ||||||
|  |         bool has_only_one_background_color = line.has_only_one_background_color(); | ||||||
|  |         if (has_only_one_background_color) | ||||||
|  |             painter.fill_rect(row_rect(row), line.attributes[0].background_color); | ||||||
|         for (word column = 0; column < m_columns; ++column) { |         for (word column = 0; column < m_columns; ++column) { | ||||||
|             auto& attribute = attribute_at(row, column); |             auto& attribute = line.attributes[column]; | ||||||
|             if (!attribute.dirty) |             line.needs_invalidation = true; | ||||||
|                 continue; |             char ch = line.characters[column]; | ||||||
|             attribute.dirty = false; |  | ||||||
|             line(row).needs_invalidation = true; |  | ||||||
|             char ch = line(row).characters[column]; |  | ||||||
|             auto character_rect = glyph_rect(row, column); |             auto character_rect = glyph_rect(row, column); | ||||||
|             auto character_background = ansi_color(attribute.background_color); |             if (!has_only_one_background_color) { | ||||||
|             painter.fill_rect(character_rect, character_background); |                 auto character_background = ansi_color(attribute.background_color); | ||||||
|  |                 painter.fill_rect(character_rect, character_background); | ||||||
|  |             } | ||||||
|             if (ch == ' ') |             if (ch == ' ') | ||||||
|                 continue; |                 continue; | ||||||
|             painter.draw_glyph(character_rect.location(), ch, ansi_color(attribute.foreground_color)); |             painter.draw_glyph(character_rect.location(), ch, ansi_color(attribute.foreground_color)); | ||||||
|  | @ -604,5 +651,5 @@ void Terminal::set_in_active_window(bool b) | ||||||
| 
 | 
 | ||||||
| void Terminal::invalidate_cursor() | void Terminal::invalidate_cursor() | ||||||
| { | { | ||||||
|     attribute_at(m_cursor_row, m_cursor_column).dirty = true; |     line(m_cursor_row).dirty = true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -52,22 +52,22 @@ private: | ||||||
|         { |         { | ||||||
|             foreground_color = 7; |             foreground_color = 7; | ||||||
|             background_color = 0; |             background_color = 0; | ||||||
|             bold = false; |             //bold = false;
 | ||||||
|             dirty = true; |  | ||||||
|         } |         } | ||||||
|         unsigned foreground_color : 4; |         unsigned foreground_color : 4; | ||||||
|         unsigned background_color : 4; |         unsigned background_color : 4; | ||||||
|         bool bold : 1; |         //bool bold : 1;
 | ||||||
|         bool dirty : 1; |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     struct Line { |     struct Line { | ||||||
|         explicit Line(word columns); |         explicit Line(word columns); | ||||||
|         ~Line(); |         ~Line(); | ||||||
|         void clear(); |         void clear(); | ||||||
|  |         bool has_only_one_background_color() const; | ||||||
|         byte* characters { nullptr }; |         byte* characters { nullptr }; | ||||||
|         Attribute* attributes { nullptr }; |         Attribute* attributes { nullptr }; | ||||||
|         bool needs_invalidation { false }; |         bool needs_invalidation { false }; | ||||||
|  |         bool dirty { false }; | ||||||
|         word length { 0 }; |         word length { 0 }; | ||||||
|     }; |     }; | ||||||
|     Line& line(size_t index) { ASSERT(index < m_rows); return *m_lines[index]; } |     Line& line(size_t index) { ASSERT(index < m_rows); return *m_lines[index]; } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling