Fix input lag: add PM_NOYIELD, skip idle GetDC, eliminate redundant renders
PeekMessage without PM_NOYIELD surrenders the timeslice on every empty queue check (~55ms per yield in Win16). Adding pm_NoYield keeps the polling loop hot so keystrokes and serial echoes are processed without scheduler delays. FlipToScreen was calling GetDC/ReleaseDC on every loop iteration even with zero dirty rows. Added early-exit scan before acquiring a DC. TickBlink was calling FlipToScreen redundantly (main loop also calls it). Removed the FlipToScreen from TickBlink and reordered the main loop to TickBlink (dirty only) then FlipToScreen (single render pass). Also: removed FBlinkOn := True reset from ParseData (was dirtying the cursor row on every incoming chunk), added WriteDeferred for parse-only without render, moved FlipToScreen from private to public, added Show call before entering the polling loop. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ec0ec8f074
commit
fbf4ed7c40
2 changed files with 36 additions and 14 deletions
|
|
@ -134,7 +134,6 @@ type
|
||||||
procedure EraseLine(Mode: Integer);
|
procedure EraseLine(Mode: Integer);
|
||||||
procedure ExecuteCSI(FinalCh: Char);
|
procedure ExecuteCSI(FinalCh: Char);
|
||||||
procedure ExecuteMusic;
|
procedure ExecuteMusic;
|
||||||
procedure FlipToScreen;
|
|
||||||
procedure FreeLineList(List: TList);
|
procedure FreeLineList(List: TList);
|
||||||
function GetCursorCol: Integer;
|
function GetCursorCol: Integer;
|
||||||
function GetCursorRow: Integer;
|
function GetCursorRow: Integer;
|
||||||
|
|
@ -166,8 +165,10 @@ type
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
procedure Clear;
|
procedure Clear;
|
||||||
procedure Reset;
|
procedure Reset;
|
||||||
|
procedure FlipToScreen;
|
||||||
procedure TickBlink;
|
procedure TickBlink;
|
||||||
procedure Write(const S: string);
|
procedure Write(const S: string);
|
||||||
|
procedure WriteDeferred(const S: string);
|
||||||
property CursorCol: Integer read GetCursorCol;
|
property CursorCol: Integer read GetCursorCol;
|
||||||
property CursorRow: Integer read GetCursorRow;
|
property CursorRow: Integer read GetCursorRow;
|
||||||
published
|
published
|
||||||
|
|
@ -1220,8 +1221,9 @@ procedure TKPAnsi.FlipToScreen;
|
||||||
{ screen via SetDIBitsToDevice immediately after rendering. One GDI call }
|
{ screen via SetDIBitsToDevice immediately after rendering. One GDI call }
|
||||||
{ per dirty row, zero for the pixel expansion itself. }
|
{ per dirty row, zero for the pixel expansion itself. }
|
||||||
var
|
var
|
||||||
DC: HDC;
|
DC: HDC;
|
||||||
Row: Integer;
|
Row: Integer;
|
||||||
|
HasDirty: Boolean;
|
||||||
begin
|
begin
|
||||||
if not HandleAllocated then
|
if not HandleAllocated then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
@ -1251,6 +1253,22 @@ begin
|
||||||
FLastCursorRow := FCursorRow;
|
FLastCursorRow := FCursorRow;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Early exit: skip GetDC/ReleaseDC when nothing needs rendering }
|
||||||
|
if not FAllDirty then
|
||||||
|
begin
|
||||||
|
HasDirty := False;
|
||||||
|
for Row := 0 to FRows - 1 do
|
||||||
|
begin
|
||||||
|
if FDirtyRow[Row] then
|
||||||
|
begin
|
||||||
|
HasDirty := True;
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if not HasDirty then
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
{ Interleaved render + blast: single buffer is reused per row }
|
{ Interleaved render + blast: single buffer is reused per row }
|
||||||
DC := GetDC(Handle);
|
DC := GetDC(Handle);
|
||||||
for Row := 0 to FRows - 1 do
|
for Row := 0 to FRows - 1 do
|
||||||
|
|
@ -1570,8 +1588,6 @@ begin
|
||||||
FAllDirty := True;
|
FAllDirty := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Reset cursor blink to visible on new data }
|
|
||||||
FBlinkOn := True;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2315,7 +2331,6 @@ begin
|
||||||
FBlinkOn := not FBlinkOn;
|
FBlinkOn := not FBlinkOn;
|
||||||
FTextBlinkOn := not FTextBlinkOn;
|
FTextBlinkOn := not FTextBlinkOn;
|
||||||
DirtyBlinkRows;
|
DirtyBlinkRows;
|
||||||
FlipToScreen;
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -2371,6 +2386,13 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TKPAnsi.WriteDeferred(const S: string);
|
||||||
|
begin
|
||||||
|
if Length(S) > 0 then
|
||||||
|
ParseData(S);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ ----------------------------------------------------------------------- }
|
{ ----------------------------------------------------------------------- }
|
||||||
{ Component registration }
|
{ Component registration }
|
||||||
{ ----------------------------------------------------------------------- }
|
{ ----------------------------------------------------------------------- }
|
||||||
|
|
|
||||||
|
|
@ -169,11 +169,12 @@ var
|
||||||
Msg: TMsg;
|
Msg: TMsg;
|
||||||
S: string;
|
S: string;
|
||||||
begin
|
begin
|
||||||
|
Show;
|
||||||
FDone := False;
|
FDone := False;
|
||||||
while not FDone do
|
while not FDone do
|
||||||
begin
|
begin
|
||||||
{ Process all pending Windows messages (keyboard, paint, scrollbar) }
|
{ Process all pending Windows messages (keyboard, paint, scrollbar) }
|
||||||
while PeekMessage(Msg, 0, 0, 0, pm_Remove) do
|
while PeekMessage(Msg, 0, 0, 0, pm_Remove or pm_NoYield) do
|
||||||
begin
|
begin
|
||||||
if Msg.message = wm_Quit then
|
if Msg.message = wm_Quit then
|
||||||
begin
|
begin
|
||||||
|
|
@ -187,18 +188,17 @@ begin
|
||||||
if FDone then
|
if FDone then
|
||||||
Break;
|
Break;
|
||||||
|
|
||||||
{ Poll serial data directly -- no WM_COMMNOTIFY, no events }
|
{ Poll serial data -- read one chunk, then yield to messages }
|
||||||
if FComm.PortOpen then
|
if FComm.PortOpen then
|
||||||
begin
|
begin
|
||||||
repeat
|
S := FComm.Input;
|
||||||
S := FComm.Input;
|
if Length(S) > 0 then
|
||||||
if Length(S) > 0 then
|
FAnsi.WriteDeferred(S);
|
||||||
FAnsi.Write(S);
|
|
||||||
until Length(S) = 0;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Tick blink state (uses GetTickCount, no WM_TIMER) }
|
{ Tick blink (dirties rows if interval elapsed), then render }
|
||||||
FAnsi.TickBlink;
|
FAnsi.TickBlink;
|
||||||
|
FAnsi.FlipToScreen;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue