Dirty Rectangle System
Unit: DRect
Optimized rendering by tracking changed screen regions.
Functions
procedure AddDirtyRect(const Rect: TRectangle);
procedure FlushDirtyRects(BackBuffer: PFrameBuffer);
procedure ClearDirtyRects;
function GetDirtyCount: Integer;
procedure MergeRectangles(R1, R2: TRectangle; var Result: TRectangle);
AddDirtyRect
Marks a rectangular region as changed. Will be copied to screen on next flush.
FlushDirtyRects
Copies all dirty rectangles from backbuffer to VGA screen memory, then clears the list.
ClearDirtyRects
Clears the dirty rectangle list without flushing to screen.
GetDirtyCount
Returns the current number of dirty rectangles in the list.
MergeRectangles
Merges two rectangles into one that encompasses both. Useful for sprite movement where you need to clear both old and new positions to avoid trails.
Example
uses VGA, DRect, Sprite;
var
BackBuffer: PFrameBuffer;
SpriteRect: TRectangle;
begin
InitVGA;
BackBuffer := CreateFrameBuffer;
{ Game loop }
while Running do
begin
{ Update sprite position }
SpriteRect.Left := SpriteX;
SpriteRect.Top := SpriteY;
SpriteRect.Right := SpriteX + SpriteWidth;
SpriteRect.Bottom := SpriteY + SpriteHeight;
{ Draw to backbuffer }
DrawBackground(BackBuffer);
DrawSprite(SpriteX, SpriteY, BackBuffer);
{ Mark changed region }
AddDirtyRect(SpriteRect);
{ Copy only dirty regions to screen }
FlushDirtyRects(BackBuffer);
ClearDirtyRects;
end;
DoneVGA;
FreeFrameBuffer(BackBuffer);
end;
Sprite Movement Example
To prevent sprite trails on slow CPUs, merge old and new sprite positions:
var
BackBuffer: PFrameBuffer;
Sprite: TSpriteInstance;
OldX, OldY: Integer;
OldRect, NewRect, MergedRect: TRectangle;
begin
{ Save old position before movement }
OldX := Sprite.X;
OldY := Sprite.Y;
{ Update sprite position }
Sprite.X := Sprite.X + VelocityX;
Sprite.Y := Sprite.Y + VelocityY;
{ Create rectangles for old and new positions }
OldRect.X := OldX;
OldRect.Y := OldY;
OldRect.Width := 32;
OldRect.Height := 32;
NewRect.X := Sprite.X;
NewRect.Y := Sprite.Y;
NewRect.Width := 32;
NewRect.Height := 32;
{ Merge into single rectangle that covers both }
MergeRectangles(OldRect, NewRect, MergedRect);
{ Clear merged area (removes old sprite) }
ClearRect(BackBuffer, MergedRect);
{ Draw sprite at new position }
DrawSprite(Sprite, BackBuffer);
{ Update only the merged region }
AddDirtyRect(MergedRect);
FlushDirtyRects(BackBuffer);
ClearDirtyRects;
end;
How It Works
AddDirtyRect: Marks a region as changed (max 256 rectangles)
FlushDirtyRects: Copies ONLY dirty regions from backbuffer to screen
ClearDirtyRects: Resets for next frame
Notes
Max 256 dirty rectangles per frame
Does NOT automatically merge overlapping regions - use
MergeRectanglesmanually when neededSignificantly faster than full-screen blit for UI/sparse updates
Used by VGAUI for widget rendering
For full-screen updates, use
RenderFrameBufferinsteadFor moving sprites, use
MergeRectanglesto combine old+new positions to prevent trails
Performance
Full screen blit: ~64000 bytes/frame
Dirty rect (1 button): ~2400 bytes/frame (26x faster)