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 }
|
||||
{ cursor positioning, color attributes, and screen manipulation. }
|
||||
{ }
|
||||
{ Immediate-mode rendering: each character run is rendered via ExtTextOut }
|
||||
{ directly to the screen DC as it arrives during parsing. WriteDeferredBuf }
|
||||
{ acquires a DC, parses data (rendering inline), and releases. No }
|
||||
{ deferred dirty-row pass needed for normal data flow. FlipToScreen only }
|
||||
{ handles blink toggle and fallback paths (scrollback view, WM_PAINT). }
|
||||
{ Split-phase rendering: WriteDeferredBuf first parses the entire input }
|
||||
{ buffer into the cell buffer (pure CPU, no GDI calls), then renders all }
|
||||
{ dirty rows in one batch via RenderRow. Scroll-ups are coalesced into }
|
||||
{ a single ScrollDC call. Separating parsing from rendering keeps the }
|
||||
{ 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. }
|
||||
|
||||
|
|
@ -101,7 +102,7 @@ type
|
|||
FDirtyRow: array[0..255] of Boolean; { True = row needs re-render }
|
||||
FAllDirty: Boolean; { True = all rows need re-render }
|
||||
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 CMFontChanged(var Msg: TMessage); message cm_FontChanged;
|
||||
|
|
@ -424,12 +425,6 @@ begin
|
|||
Line^.Cells[I].Blink := False;
|
||||
end;
|
||||
end;
|
||||
if FLiveDC <> 0 then
|
||||
begin
|
||||
FlushPendingScrolls;
|
||||
RenderRow(FLiveDC, FCursorRow);
|
||||
end
|
||||
else
|
||||
FDirtyRow[FCursorRow] := True;
|
||||
end;
|
||||
|
||||
|
|
@ -454,17 +449,8 @@ begin
|
|||
FScreen.Add(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
|
||||
FDirtyRow[J] := True;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
|
|
@ -524,9 +510,6 @@ end;
|
|||
procedure TKPAnsi.DoScrollDown;
|
||||
var
|
||||
Line: PTermLine;
|
||||
ScrollR: TRect;
|
||||
ClipR: TRect;
|
||||
UpdateR: TRect;
|
||||
begin
|
||||
if FScreen.Count < FRows then
|
||||
Exit;
|
||||
|
|
@ -538,18 +521,6 @@ begin
|
|||
GetMem(Line, SizeOf(TTermLineRec));
|
||||
AllocLine(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;
|
||||
end;
|
||||
|
||||
|
|
@ -638,24 +609,6 @@ begin
|
|||
UpdateScrollbar;
|
||||
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
|
||||
0:
|
||||
for I := FCursorRow to FRows - 1 do
|
||||
|
|
@ -666,14 +619,12 @@ begin
|
|||
2:
|
||||
FAllDirty := True;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKPAnsi.EraseLine(Mode: Integer);
|
||||
var
|
||||
J: Integer;
|
||||
R: TRect;
|
||||
Line: PTermLine;
|
||||
begin
|
||||
Line := FScreen[FCursorRow];
|
||||
|
|
@ -703,36 +654,6 @@ begin
|
|||
2: { Erase entire line }
|
||||
AllocLine(Line);
|
||||
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;
|
||||
end;
|
||||
|
||||
|
|
@ -1071,13 +992,13 @@ var
|
|||
ClipR: TRect;
|
||||
UpdateR: TRect;
|
||||
Row: Integer;
|
||||
GhostRow: Integer;
|
||||
begin
|
||||
if (FPendingScrolls = 0) or (FLiveDC = 0) then
|
||||
Exit;
|
||||
if FPendingScrolls >= FRows then
|
||||
begin
|
||||
for Row := 0 to FRows - 1 do
|
||||
RenderRow(FLiveDC, Row);
|
||||
FAllDirty := True;
|
||||
FPendingScrolls := 0;
|
||||
Exit;
|
||||
end;
|
||||
|
|
@ -1088,8 +1009,13 @@ begin
|
|||
ClipR := ScrollR;
|
||||
ScrollDC(FLiveDC, 0, -(FPendingScrolls * FCellHeight),
|
||||
ScrollR, ClipR, 0, @UpdateR);
|
||||
{ Mark exposed rows and cursor ghost row for deferred rendering }
|
||||
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;
|
||||
end;
|
||||
|
||||
|
|
@ -1241,12 +1167,6 @@ begin
|
|||
Line^.Cells[I].Blink := False;
|
||||
end;
|
||||
end;
|
||||
if FLiveDC <> 0 then
|
||||
begin
|
||||
FlushPendingScrolls;
|
||||
RenderRow(FLiveDC, FCursorRow);
|
||||
end
|
||||
else
|
||||
FDirtyRow[FCursorRow] := True;
|
||||
end;
|
||||
|
||||
|
|
@ -1271,17 +1191,8 @@ begin
|
|||
AllocLine(Line);
|
||||
FScreen.Insert(FCursorRow, Line);
|
||||
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
|
||||
FDirtyRow[J] := True;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
|
|
@ -1367,6 +1278,7 @@ end;
|
|||
|
||||
procedure TKPAnsi.Paint;
|
||||
var
|
||||
DC: HDC;
|
||||
Row: Integer;
|
||||
begin
|
||||
if FPaintFont = 0 then
|
||||
|
|
@ -1409,9 +1321,6 @@ var
|
|||
BGIdx: Byte;
|
||||
RunEnd: Integer;
|
||||
Remaining: Integer;
|
||||
RunStartI: Integer;
|
||||
RunStartCol: Integer;
|
||||
R: TRect;
|
||||
begin
|
||||
Line := nil;
|
||||
I := 0;
|
||||
|
|
@ -1458,10 +1367,6 @@ begin
|
|||
(RunEnd - I < Remaining) do
|
||||
Inc(RunEnd);
|
||||
|
||||
{ Save run start for immediate rendering }
|
||||
RunStartI := I;
|
||||
RunStartCol := FCursorCol;
|
||||
|
||||
{ Fill cells in tight loop }
|
||||
if FAttrReverse then
|
||||
begin
|
||||
|
|
@ -1490,28 +1395,6 @@ begin
|
|||
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;
|
||||
end
|
||||
else if Buf[I] = #27 then
|
||||
|
|
@ -2289,23 +2172,35 @@ end;
|
|||
|
||||
|
||||
procedure TKPAnsi.WriteDeferredBuf(Buf: PChar; Len: Integer);
|
||||
var
|
||||
Row: Integer;
|
||||
begin
|
||||
if Len > 0 then
|
||||
begin
|
||||
if Len <= 0 then
|
||||
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
|
||||
begin
|
||||
FLiveDC := GetDC(Handle);
|
||||
SelectObject(FLiveDC, FPaintFont);
|
||||
SetBkMode(FLiveDC, OPAQUE);
|
||||
end;
|
||||
ParseDataBuf(Buf, Len);
|
||||
if FLiveDC <> 0 then
|
||||
begin
|
||||
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);
|
||||
FLiveDC := 0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue