Split parsing from rendering to avoid GDI/CPU cache thrashing
ParseDataBuf is now pure CPU work with no GDI calls -- the parsing loop stays cache-hot in the 486 L1. WriteDeferredBuf parses first, then renders all dirty rows in one batch. Removes ~105 lines of inline FLiveDC rendering from 8 methods. Also fixes missing DC local variable in Paint. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3fc2b410ba
commit
cb2018dff4
1 changed files with 63 additions and 168 deletions
|
|
@ -7,11 +7,12 @@ unit KPAnsi;
|
||||||
{ Renders incoming data using standard ANSI/VT100 escape sequences for }
|
{ Renders incoming data using standard ANSI/VT100 escape sequences for }
|
||||||
{ cursor positioning, color attributes, and screen manipulation. }
|
{ cursor positioning, color attributes, and screen manipulation. }
|
||||||
{ }
|
{ }
|
||||||
{ Immediate-mode rendering: each character run is rendered via ExtTextOut }
|
{ Split-phase rendering: WriteDeferredBuf first parses the entire input }
|
||||||
{ directly to the screen DC as it arrives during parsing. WriteDeferredBuf }
|
{ buffer into the cell buffer (pure CPU, no GDI calls), then renders all }
|
||||||
{ acquires a DC, parses data (rendering inline), and releases. No }
|
{ dirty rows in one batch via RenderRow. Scroll-ups are coalesced into }
|
||||||
{ deferred dirty-row pass needed for normal data flow. FlipToScreen only }
|
{ a single ScrollDC call. Separating parsing from rendering keeps the }
|
||||||
{ handles blink toggle and fallback paths (scrollback view, WM_PAINT). }
|
{ parsing loop cache-hot and avoids interleaving GDI kernel transitions }
|
||||||
|
{ with CPU work. FlipToScreen handles blink and fallback paths. }
|
||||||
{ }
|
{ }
|
||||||
{ Installs to the "KP" palette tab alongside TKPComm. }
|
{ Installs to the "KP" palette tab alongside TKPComm. }
|
||||||
|
|
||||||
|
|
@ -101,7 +102,7 @@ type
|
||||||
FDirtyRow: array[0..255] of Boolean; { True = row needs re-render }
|
FDirtyRow: array[0..255] of Boolean; { True = row needs re-render }
|
||||||
FAllDirty: Boolean; { True = all rows need re-render }
|
FAllDirty: Boolean; { True = all rows need re-render }
|
||||||
FScrollbarDirty: Boolean; { True = scrollbar range/position needs update }
|
FScrollbarDirty: Boolean; { True = scrollbar range/position needs update }
|
||||||
FLiveDC: HDC; { Non-zero during immediate rendering }
|
FLiveDC: HDC; { Non-zero during render pass in WriteDeferredBuf }
|
||||||
|
|
||||||
procedure AllocLine(Line: PTermLine);
|
procedure AllocLine(Line: PTermLine);
|
||||||
procedure CMFontChanged(var Msg: TMessage); message cm_FontChanged;
|
procedure CMFontChanged(var Msg: TMessage); message cm_FontChanged;
|
||||||
|
|
@ -424,12 +425,6 @@ begin
|
||||||
Line^.Cells[I].Blink := False;
|
Line^.Cells[I].Blink := False;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
if FLiveDC <> 0 then
|
|
||||||
begin
|
|
||||||
FlushPendingScrolls;
|
|
||||||
RenderRow(FLiveDC, FCursorRow);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
FDirtyRow[FCursorRow] := True;
|
FDirtyRow[FCursorRow] := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -454,18 +449,9 @@ begin
|
||||||
FScreen.Add(Line);
|
FScreen.Add(Line);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
if FLiveDC <> 0 then
|
|
||||||
begin
|
|
||||||
FlushPendingScrolls;
|
|
||||||
for J := FCursorRow to FRows - 1 do
|
|
||||||
RenderRow(FLiveDC, J);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
for J := FCursorRow to FRows - 1 do
|
for J := FCursorRow to FRows - 1 do
|
||||||
FDirtyRow[J] := True;
|
FDirtyRow[J] := True;
|
||||||
end;
|
end;
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
destructor TKPAnsi.Destroy;
|
destructor TKPAnsi.Destroy;
|
||||||
|
|
@ -524,9 +510,6 @@ end;
|
||||||
procedure TKPAnsi.DoScrollDown;
|
procedure TKPAnsi.DoScrollDown;
|
||||||
var
|
var
|
||||||
Line: PTermLine;
|
Line: PTermLine;
|
||||||
ScrollR: TRect;
|
|
||||||
ClipR: TRect;
|
|
||||||
UpdateR: TRect;
|
|
||||||
begin
|
begin
|
||||||
if FScreen.Count < FRows then
|
if FScreen.Count < FRows then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
@ -538,18 +521,6 @@ begin
|
||||||
GetMem(Line, SizeOf(TTermLineRec));
|
GetMem(Line, SizeOf(TTermLineRec));
|
||||||
AllocLine(Line);
|
AllocLine(Line);
|
||||||
FScreen.Insert(0, Line);
|
FScreen.Insert(0, Line);
|
||||||
if FLiveDC <> 0 then
|
|
||||||
begin
|
|
||||||
FlushPendingScrolls;
|
|
||||||
ScrollR.Left := 0;
|
|
||||||
ScrollR.Top := 0;
|
|
||||||
ScrollR.Right := FCols * FCellWidth;
|
|
||||||
ScrollR.Bottom := FRows * FCellHeight;
|
|
||||||
ClipR := ScrollR;
|
|
||||||
ScrollDC(FLiveDC, 0, FCellHeight, ScrollR, ClipR, 0, @UpdateR);
|
|
||||||
RenderRow(FLiveDC, 0);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
FAllDirty := True;
|
FAllDirty := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -638,24 +609,6 @@ begin
|
||||||
UpdateScrollbar;
|
UpdateScrollbar;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
{ Immediate render or deferred dirty }
|
|
||||||
if FLiveDC <> 0 then
|
|
||||||
begin
|
|
||||||
FlushPendingScrolls;
|
|
||||||
case Mode of
|
|
||||||
0:
|
|
||||||
for I := FCursorRow to FRows - 1 do
|
|
||||||
RenderRow(FLiveDC, I);
|
|
||||||
1:
|
|
||||||
for I := 0 to FCursorRow do
|
|
||||||
RenderRow(FLiveDC, I);
|
|
||||||
2:
|
|
||||||
for I := 0 to FRows - 1 do
|
|
||||||
RenderRow(FLiveDC, I);
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
case Mode of
|
case Mode of
|
||||||
0:
|
0:
|
||||||
for I := FCursorRow to FRows - 1 do
|
for I := FCursorRow to FRows - 1 do
|
||||||
|
|
@ -667,13 +620,11 @@ begin
|
||||||
FAllDirty := True;
|
FAllDirty := True;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TKPAnsi.EraseLine(Mode: Integer);
|
procedure TKPAnsi.EraseLine(Mode: Integer);
|
||||||
var
|
var
|
||||||
J: Integer;
|
J: Integer;
|
||||||
R: TRect;
|
|
||||||
Line: PTermLine;
|
Line: PTermLine;
|
||||||
begin
|
begin
|
||||||
Line := FScreen[FCursorRow];
|
Line := FScreen[FCursorRow];
|
||||||
|
|
@ -703,36 +654,6 @@ begin
|
||||||
2: { Erase entire line }
|
2: { Erase entire line }
|
||||||
AllocLine(Line);
|
AllocLine(Line);
|
||||||
end;
|
end;
|
||||||
if FLiveDC <> 0 then
|
|
||||||
begin
|
|
||||||
FlushPendingScrolls;
|
|
||||||
SetBkColor(FLiveDC, AnsiColors[0]);
|
|
||||||
case Mode of
|
|
||||||
0:
|
|
||||||
begin
|
|
||||||
R.Left := FCursorCol * FCellWidth;
|
|
||||||
R.Top := FCursorRow * FCellHeight;
|
|
||||||
R.Right := FCols * FCellWidth;
|
|
||||||
R.Bottom := R.Top + FCellHeight;
|
|
||||||
end;
|
|
||||||
1:
|
|
||||||
begin
|
|
||||||
R.Left := 0;
|
|
||||||
R.Top := FCursorRow * FCellHeight;
|
|
||||||
R.Right := (FCursorCol + 1) * FCellWidth;
|
|
||||||
R.Bottom := R.Top + FCellHeight;
|
|
||||||
end;
|
|
||||||
2:
|
|
||||||
begin
|
|
||||||
R.Left := 0;
|
|
||||||
R.Top := FCursorRow * FCellHeight;
|
|
||||||
R.Right := FCols * FCellWidth;
|
|
||||||
R.Bottom := R.Top + FCellHeight;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
ExtTextOut(FLiveDC, R.Left, R.Top, ETO_OPAQUE, @R, nil, 0, nil);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
FDirtyRow[FCursorRow] := True;
|
FDirtyRow[FCursorRow] := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -1071,13 +992,13 @@ var
|
||||||
ClipR: TRect;
|
ClipR: TRect;
|
||||||
UpdateR: TRect;
|
UpdateR: TRect;
|
||||||
Row: Integer;
|
Row: Integer;
|
||||||
|
GhostRow: Integer;
|
||||||
begin
|
begin
|
||||||
if (FPendingScrolls = 0) or (FLiveDC = 0) then
|
if (FPendingScrolls = 0) or (FLiveDC = 0) then
|
||||||
Exit;
|
Exit;
|
||||||
if FPendingScrolls >= FRows then
|
if FPendingScrolls >= FRows then
|
||||||
begin
|
begin
|
||||||
for Row := 0 to FRows - 1 do
|
FAllDirty := True;
|
||||||
RenderRow(FLiveDC, Row);
|
|
||||||
FPendingScrolls := 0;
|
FPendingScrolls := 0;
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
|
|
@ -1088,8 +1009,13 @@ begin
|
||||||
ClipR := ScrollR;
|
ClipR := ScrollR;
|
||||||
ScrollDC(FLiveDC, 0, -(FPendingScrolls * FCellHeight),
|
ScrollDC(FLiveDC, 0, -(FPendingScrolls * FCellHeight),
|
||||||
ScrollR, ClipR, 0, @UpdateR);
|
ScrollR, ClipR, 0, @UpdateR);
|
||||||
|
{ Mark exposed rows and cursor ghost row for deferred rendering }
|
||||||
for Row := FRows - FPendingScrolls to FRows - 1 do
|
for Row := FRows - FPendingScrolls to FRows - 1 do
|
||||||
RenderRow(FLiveDC, Row);
|
FDirtyRow[Row] := True;
|
||||||
|
GhostRow := FLastCursorRow - FPendingScrolls;
|
||||||
|
if (GhostRow >= 0) and (GhostRow < FRows) then
|
||||||
|
FDirtyRow[GhostRow] := True;
|
||||||
|
FLastCursorRow := FCursorRow;
|
||||||
FPendingScrolls := 0;
|
FPendingScrolls := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -1241,12 +1167,6 @@ begin
|
||||||
Line^.Cells[I].Blink := False;
|
Line^.Cells[I].Blink := False;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
if FLiveDC <> 0 then
|
|
||||||
begin
|
|
||||||
FlushPendingScrolls;
|
|
||||||
RenderRow(FLiveDC, FCursorRow);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
FDirtyRow[FCursorRow] := True;
|
FDirtyRow[FCursorRow] := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -1271,18 +1191,9 @@ begin
|
||||||
AllocLine(Line);
|
AllocLine(Line);
|
||||||
FScreen.Insert(FCursorRow, Line);
|
FScreen.Insert(FCursorRow, Line);
|
||||||
end;
|
end;
|
||||||
if FLiveDC <> 0 then
|
|
||||||
begin
|
|
||||||
FlushPendingScrolls;
|
|
||||||
for J := FCursorRow to FRows - 1 do
|
|
||||||
RenderRow(FLiveDC, J);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
for J := FCursorRow to FRows - 1 do
|
for J := FCursorRow to FRows - 1 do
|
||||||
FDirtyRow[J] := True;
|
FDirtyRow[J] := True;
|
||||||
end;
|
end;
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TKPAnsi.KeyDown(var Key: Word; Shift: TShiftState);
|
procedure TKPAnsi.KeyDown(var Key: Word; Shift: TShiftState);
|
||||||
|
|
@ -1367,6 +1278,7 @@ end;
|
||||||
|
|
||||||
procedure TKPAnsi.Paint;
|
procedure TKPAnsi.Paint;
|
||||||
var
|
var
|
||||||
|
DC: HDC;
|
||||||
Row: Integer;
|
Row: Integer;
|
||||||
begin
|
begin
|
||||||
if FPaintFont = 0 then
|
if FPaintFont = 0 then
|
||||||
|
|
@ -1409,9 +1321,6 @@ var
|
||||||
BGIdx: Byte;
|
BGIdx: Byte;
|
||||||
RunEnd: Integer;
|
RunEnd: Integer;
|
||||||
Remaining: Integer;
|
Remaining: Integer;
|
||||||
RunStartI: Integer;
|
|
||||||
RunStartCol: Integer;
|
|
||||||
R: TRect;
|
|
||||||
begin
|
begin
|
||||||
Line := nil;
|
Line := nil;
|
||||||
I := 0;
|
I := 0;
|
||||||
|
|
@ -1458,10 +1367,6 @@ begin
|
||||||
(RunEnd - I < Remaining) do
|
(RunEnd - I < Remaining) do
|
||||||
Inc(RunEnd);
|
Inc(RunEnd);
|
||||||
|
|
||||||
{ Save run start for immediate rendering }
|
|
||||||
RunStartI := I;
|
|
||||||
RunStartCol := FCursorCol;
|
|
||||||
|
|
||||||
{ Fill cells in tight loop }
|
{ Fill cells in tight loop }
|
||||||
if FAttrReverse then
|
if FAttrReverse then
|
||||||
begin
|
begin
|
||||||
|
|
@ -1490,28 +1395,6 @@ begin
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Immediate render or deferred dirty }
|
|
||||||
if FLiveDC <> 0 then
|
|
||||||
begin
|
|
||||||
FlushPendingScrolls;
|
|
||||||
if FAttrReverse then
|
|
||||||
begin
|
|
||||||
SetTextColor(FLiveDC, AnsiColors[BGIdx]);
|
|
||||||
SetBkColor(FLiveDC, AnsiColors[FGIdx]);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SetTextColor(FLiveDC, AnsiColors[FGIdx]);
|
|
||||||
SetBkColor(FLiveDC, AnsiColors[BGIdx]);
|
|
||||||
end;
|
|
||||||
R.Left := RunStartCol * FCellWidth;
|
|
||||||
R.Top := FCursorRow * FCellHeight;
|
|
||||||
R.Right := FCursorCol * FCellWidth;
|
|
||||||
R.Bottom := R.Top + FCellHeight;
|
|
||||||
ExtTextOut(FLiveDC, R.Left, R.Top, ETO_OPAQUE, @R,
|
|
||||||
@Buf[RunStartI], I - RunStartI, nil);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
FDirtyRow[FCursorRow] := True;
|
FDirtyRow[FCursorRow] := True;
|
||||||
end
|
end
|
||||||
else if Buf[I] = #27 then
|
else if Buf[I] = #27 then
|
||||||
|
|
@ -2289,24 +2172,36 @@ end;
|
||||||
|
|
||||||
|
|
||||||
procedure TKPAnsi.WriteDeferredBuf(Buf: PChar; Len: Integer);
|
procedure TKPAnsi.WriteDeferredBuf(Buf: PChar; Len: Integer);
|
||||||
|
var
|
||||||
|
Row: Integer;
|
||||||
begin
|
begin
|
||||||
if Len > 0 then
|
if Len <= 0 then
|
||||||
begin
|
Exit;
|
||||||
|
|
||||||
|
{ Parse into cell buffer -- pure CPU, no GDI calls }
|
||||||
|
ParseDataBuf(Buf, Len);
|
||||||
|
|
||||||
|
{ Render pass: flush coalesced scrolls, then redraw dirty rows }
|
||||||
if HandleAllocated and (FPaintFont <> 0) and (FScrollPos = 0) then
|
if HandleAllocated and (FPaintFont <> 0) and (FScrollPos = 0) then
|
||||||
begin
|
begin
|
||||||
FLiveDC := GetDC(Handle);
|
FLiveDC := GetDC(Handle);
|
||||||
SelectObject(FLiveDC, FPaintFont);
|
SelectObject(FLiveDC, FPaintFont);
|
||||||
SetBkMode(FLiveDC, OPAQUE);
|
SetBkMode(FLiveDC, OPAQUE);
|
||||||
end;
|
|
||||||
ParseDataBuf(Buf, Len);
|
|
||||||
if FLiveDC <> 0 then
|
|
||||||
begin
|
|
||||||
FlushPendingScrolls;
|
FlushPendingScrolls;
|
||||||
|
for Row := 0 to FRows - 1 do
|
||||||
|
begin
|
||||||
|
if FAllDirty or FDirtyRow[Row] then
|
||||||
|
begin
|
||||||
|
RenderRow(FLiveDC, Row);
|
||||||
|
FDirtyRow[Row] := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
FAllDirty := False;
|
||||||
|
FLastCursorRow := FCursorRow;
|
||||||
ReleaseDC(Handle, FLiveDC);
|
ReleaseDC(Handle, FLiveDC);
|
||||||
FLiveDC := 0;
|
FLiveDC := 0;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
{ ----------------------------------------------------------------------- }
|
{ ----------------------------------------------------------------------- }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue