Implement framebuffer double buffering
All checks were successful
Builds / ExectOS (amd64, release) (push) Successful in -59m26s
Builds / ExectOS (amd64, debug) (push) Successful in -59m24s
Builds / ExectOS (i686, release) (push) Successful in -59m27s
Builds / ExectOS (i686, debug) (push) Successful in -59m25s

This commit is contained in:
2026-05-04 11:51:52 +02:00
parent a7151dbc89
commit 1050ddea8a
5 changed files with 260 additions and 61 deletions

View File

@@ -33,6 +33,9 @@ KAFFINITY HL::Cpu::ActiveProcessors;
/* FrameBuffer information */
HL_FRAMEBUFFER_DATA HL::FrameBuffer::FrameBufferData;
/* Pointer to the RAM shadow buffer used for double-buffered rendering */
PVOID HL::FrameBuffer::ScreenShadowBuffer;
/* Scroll region information */
HL_SCROLL_REGION_DATA HL::FrameBuffer::ScrollRegionData;

View File

@@ -25,10 +25,9 @@ XTAPI
VOID
HL::FrameBuffer::ClearScreen(IN ULONG Color)
{
ULONG PositionX, PositionY;
ULONG BackgroundColor;
PCHAR CurrentLine;
PULONG Pixel;
ULONG BackgroundColor, PositionY;
PCHAR CurrentLine, TargetBuffer;
UCHAR FillByte;
/* Make sure frame buffer is already initialized */
if(FrameBufferData.Initialized == FALSE)
@@ -37,20 +36,29 @@ HL::FrameBuffer::ClearScreen(IN ULONG Color)
return;
}
/* Convert background color and get pointer to frame buffer */
/* Convert background color */
BackgroundColor = GetRGBColor(Color);
CurrentLine = (PCHAR)FrameBufferData.Address;
/* Extract the lower byte for SetMemory */
FillByte = (UCHAR)(BackgroundColor & 0xFF);
/* Determine target buffer */
TargetBuffer = (ScreenShadowBuffer != NULLPTR) ? (PCHAR)ScreenShadowBuffer : (PCHAR)FrameBufferData.Address;
CurrentLine = TargetBuffer;
/* Fill the screen with the specified color */
for(PositionY = 0; PositionY < FrameBufferData.Height; PositionY++, CurrentLine += FrameBufferData.Pitch)
{
/* Fill the current line with the specified color */
Pixel = (PULONG)CurrentLine;
for(PositionX = 0; PositionX < FrameBufferData.Width; PositionX++)
{
/* Set the color of the pixel */
Pixel[PositionX] = BackgroundColor;
/* Fill the current scanline with the background color byte */
RTL::Memory::SetMemory(CurrentLine, FillByte, FrameBufferData.Width * FrameBufferData.BytesPerPixel);
}
/* Check if Shadow Buffer is active */
if(ScreenShadowBuffer != NULLPTR)
{
/* Flush changes to VRAM */
RTL::Memory::CopyMemory(FrameBufferData.Address, ScreenShadowBuffer,
FrameBufferData.Pitch * FrameBufferData.Height);
}
}
@@ -68,7 +76,9 @@ XTCDECL
XTSTATUS
HL::FrameBuffer::DisplayCharacter(IN WCHAR Character)
{
ULONG CharacterX, CharacterY;
PSSFN_FONT_HEADER FbFont;
BOOLEAN VisibleCharacter;
/* Make sure frame buffer is already initialized */
if(FrameBufferData.Initialized == FALSE)
@@ -80,6 +90,9 @@ HL::FrameBuffer::DisplayCharacter(IN WCHAR Character)
/* Get font information */
FbFont = (PSSFN_FONT_HEADER)FrameBufferData.Font;
/* Assume invisible character */
VisibleCharacter = FALSE;
/* Handle special characters */
switch(Character)
{
@@ -98,8 +111,13 @@ HL::FrameBuffer::DisplayCharacter(IN WCHAR Character)
}
break;
default:
/* Draw the character */
DrawCharacter(ScrollRegionData.CursorX, ScrollRegionData.CursorY, ScrollRegionData.TextColor, Character);
/* Save cursor position */
CharacterX = ScrollRegionData.CursorX;
CharacterY = ScrollRegionData.CursorY;
/* Draw the character to RAM and mark it as visible */
DrawCharacter(CharacterX, CharacterY, ScrollRegionData.TextColor, Character);
VisibleCharacter = TRUE;
/* Advance cursor */
ScrollRegionData.CursorX += FbFont->Width;
@@ -107,6 +125,7 @@ HL::FrameBuffer::DisplayCharacter(IN WCHAR Character)
/* Check if cursor reached end of line */
if(ScrollRegionData.CursorX >= ScrollRegionData.Right)
{
/* Reset cursor to the left margin and advance to the next line */
ScrollRegionData.CursorX = ScrollRegionData.Left;
ScrollRegionData.CursorY += FbFont->Height;
}
@@ -120,6 +139,13 @@ HL::FrameBuffer::DisplayCharacter(IN WCHAR Character)
ScrollRegion();
ScrollRegionData.CursorY = ScrollRegionData.Bottom - FbFont->Height;
}
else if(VisibleCharacter == TRUE)
{
/* Flush visible character to VRAM */
UpdateScreenRegion(CharacterX, CharacterY,
CharacterX + FbFont->Width,
CharacterY + FbFont->Height);
}
/* Return success */
return STATUS_SUCCESS;
@@ -153,9 +179,8 @@ HL::FrameBuffer::DrawCharacter(IN ULONG PositionX,
{
UINT CurrentFragment, Glyph, GlyphLimit, Index, Line, Mapping;
PUCHAR Character, CharacterMapping, Fragment;
UINT_PTR GlyphPixel, Pixel;
ULONG FontColor, GlyphOffset, PixelOffset;
PSSFN_FONT_HEADER FbFont;
ULONG FontColor;
/* Make sure frame buffer is already initialized */
if(FrameBufferData.Initialized == FALSE)
@@ -192,7 +217,7 @@ HL::FrameBuffer::DrawCharacter(IN ULONG PositionX,
}
else
{
/* There's a glyph for this character, check if it matches */
/* There is a glyph for this character, check if it matches */
if(Index == WideCharacter)
{
/* Found the character, break loop */
@@ -213,8 +238,7 @@ HL::FrameBuffer::DrawCharacter(IN ULONG PositionX,
}
/* Find the glyph position on the frame buffer and set font color */
GlyphPixel = (UINT_PTR)FrameBufferData.Address + PositionY * FrameBufferData.Pitch +
PositionX * FrameBufferData.BytesPerPixel;
GlyphOffset = (PositionY * FrameBufferData.Pitch) + (PositionX * FrameBufferData.BytesPerPixel);
FontColor = GetRGBColor(Color);
/* Check all kerning fragments */
@@ -243,7 +267,7 @@ HL::FrameBuffer::DrawCharacter(IN ULONG PositionX,
}
/* Get initial glyph line */
GlyphPixel += (CharacterMapping[1] - Mapping) * FrameBufferData.Pitch;
GlyphOffset += (CharacterMapping[1] - Mapping) * FrameBufferData.Pitch;
Mapping = CharacterMapping[1];
/* Extract glyph data from fragments table and advance */
@@ -255,7 +279,8 @@ HL::FrameBuffer::DrawCharacter(IN ULONG PositionX,
CurrentFragment = 1;
while(GlyphLimit--)
{
Pixel = GlyphPixel;
/* Set the initial pixel offset for the current glyph fragment */
PixelOffset = GlyphOffset;
for(Line = 0; Line < Glyph; Line++)
{
/* Decode compressed offsets */
@@ -269,17 +294,26 @@ HL::FrameBuffer::DrawCharacter(IN ULONG PositionX,
/* Check if pixel should be drawn */
if(*Fragment & CurrentFragment)
{
/* Draw glyph pixel */
*((PULONG)Pixel) = FontColor;
/* Route the pixel draw operation to the active buffer */
if(ScreenShadowBuffer != NULLPTR)
{
/* Draw glyph pixel to Shadow Buffer */
*((PULONG)((PCHAR)ScreenShadowBuffer + PixelOffset)) = FontColor;
}
else
{
/* Draw glyph pixel directly to VRAM */
*((PULONG)((PCHAR)FrameBufferData.Address + PixelOffset)) = FontColor;
}
}
/* Advance pixel pointer */
Pixel += FrameBufferData.BytesPerPixel;
PixelOffset += FrameBufferData.BytesPerPixel;
CurrentFragment <<= 1;
}
/* Advance to next line and increase mapping */
GlyphPixel += FrameBufferData.Pitch;
GlyphOffset += FrameBufferData.Pitch;
Mapping++;
}
@@ -310,7 +344,7 @@ HL::FrameBuffer::DrawPixel(IN ULONG PositionX,
IN ULONG PositionY,
IN ULONG Color)
{
PCHAR PixelAddress;
ULONG Offset;
/* Make sure frame buffer is already initialized */
if(FrameBufferData.Initialized == FALSE)
@@ -327,11 +361,67 @@ HL::FrameBuffer::DrawPixel(IN ULONG PositionX,
}
/* Calculate the address of the pixel in the frame buffer memory */
PixelAddress = (PCHAR)FrameBufferData.Address + (PositionY * FrameBufferData.Pitch) +
(PositionX * FrameBufferData.BytesPerPixel);
Offset = (PositionY * FrameBufferData.Pitch) + (PositionX * FrameBufferData.BytesPerPixel);
/* Set the color of the pixel by writing to the corresponding memory location */
*((PULONG)PixelAddress) = GetRGBColor(Color);
/* Route the pixel draw operation to the active buffer */
if(ScreenShadowBuffer != NULLPTR)
{
/* Set the color of the pixel by writing to the corresponding memory location (RAM) */
*((PULONG)((PCHAR)ScreenShadowBuffer + Offset)) = GetRGBColor(Color);
}
else
{
/* Set the color of the pixel by writing to the corresponding memory location (VRAM) */
*((PULONG)((PCHAR)FrameBufferData.Address + Offset)) = GetRGBColor(Color);
}
}
/**
* Enables the Shadow Buffer (Double Buffering) for high-performance rendering.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
HL::FrameBuffer::EnableShadowBuffer(VOID)
{
ULONG FrameBufferSize;
XTSTATUS Status;
/* Check if the shadow buffer is already enabled */
if(ScreenShadowBuffer != NULLPTR)
{
/* Nothing to do, return success */
return STATUS_SUCCESS;
}
/* Make sure frame buffer is already initialized */
if(FrameBufferData.Initialized == FALSE)
{
/* Unable to operate on non-initialized frame buffer */
return STATUS_DEVICE_NOT_READY;
}
/* Calculate the total size of the framebuffer */
FrameBufferSize = FrameBufferData.Pitch * FrameBufferData.Height;
/* Allocate non-paged memory for the shadow buffer */
Status = MM::Allocator::AllocatePool(NonPagedPool, FrameBufferSize,
&ScreenShadowBuffer, SIGNATURE32('F', 'B', 'U', 'F'));
if(Status != STATUS_SUCCESS)
{
/* Allocation failed, return status code */
ScreenShadowBuffer = NULLPTR;
return Status;
}
/* Synchronize the newly allocated shadow buffer with the current on-screen contents */
RTL::Memory::CopyMemory(ScreenShadowBuffer, FrameBufferData.Address, FrameBufferSize);
/* Return success */
return STATUS_SUCCESS;
}
/**
@@ -544,10 +634,9 @@ XTAPI
VOID
HL::FrameBuffer::ScrollRegion(VOID)
{
PCHAR Destination, Source;
PCHAR TargetBuffer, Destination, Source;
ULONG Line, PositionX, LineBytes;
PSSFN_FONT_HEADER FbFont;
ULONG Line, PositionX;
ULONG LineBytes;
PULONG Pixel;
/* Make sure frame buffer is already initialized */
@@ -557,37 +646,132 @@ HL::FrameBuffer::ScrollRegion(VOID)
return;
}
/* Get font information */
/* Retrieve font metrics and calculate line properties for the scroll operation */
FbFont = (PSSFN_FONT_HEADER)FrameBufferData.Font;
/* Calculate bytes per line in the scroll region */
LineBytes = (ScrollRegionData.Right - ScrollRegionData.Left) * FrameBufferData.BytesPerPixel;
TargetBuffer = (ScreenShadowBuffer != NULLPTR) ? (PCHAR)ScreenShadowBuffer : (PCHAR)FrameBufferData.Address;
/* Scroll up each scan line in the scroll region */
for(Line = ScrollRegionData.Top; Line < ScrollRegionData.Bottom - FbFont->Height; Line++)
/* Process every line in the scroll region */
for(Line = ScrollRegionData.Top; Line < ScrollRegionData.Bottom; Line++)
{
Destination = (PCHAR)FrameBufferData.Address + Line * FrameBufferData.Pitch +
ScrollRegionData.Left * FrameBufferData.BytesPerPixel;
/* Calculate destination address for the current line */
Destination = TargetBuffer + (Line * FrameBufferData.Pitch) +
(ScrollRegionData.Left * FrameBufferData.BytesPerPixel);
/* The source is one full text line (FbFont->Height) below the destination */
Source = (PCHAR)FrameBufferData.Address + (Line + FbFont->Height) * FrameBufferData.Pitch +
ScrollRegionData.Left * FrameBufferData.BytesPerPixel;
/* Move each scan line in the scroll region up */
RTL::Memory::MoveMemory(Destination, Source, LineBytes);
/* Check if the current line needs to be copied from below or cleared */
if(Line < ScrollRegionData.Bottom - FbFont->Height)
{
/* Copy the line from below */
Source = Destination + (FbFont->Height * FrameBufferData.Pitch);
RTL::Memory::CopyMemory(Destination, Source, LineBytes);
}
/* Clear the last text line */
for(Line = ScrollRegionData.Bottom - FbFont->Height; Line < ScrollRegionData.Bottom; Line++)
else
{
/* Get pointer to the start of the scan line to clear */
Pixel = (PULONG)((PCHAR)FrameBufferData.Address + Line * FrameBufferData.Pitch +
ScrollRegionData.Left * FrameBufferData.BytesPerPixel);
/* Clear each pixel in the scan line with the background color */
/* Fill the bottom line(s) with the background color */
Pixel = (PULONG)Destination;
for(PositionX = 0; PositionX < (ScrollRegionData.Right - ScrollRegionData.Left); PositionX++)
{
/* Overwrite the pixel with the background color */
Pixel[PositionX] = ScrollRegionData.BackgroundColor;
}
}
}
/* Flush changes to VRAM if Shadow Buffer was used */
if(ScreenShadowBuffer != NULLPTR)
{
/* Flush the updated scroll region to VRAM */
UpdateScreenRegion(ScrollRegionData.Left,
ScrollRegionData.Top,
ScrollRegionData.Right,
ScrollRegionData.Bottom);
}
}
/**
* Flushes the current content of the Shadow Buffer to the visible FrameBuffer (VRAM).
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
HL::FrameBuffer::UpdateScreen(VOID)
{
/* Make sure framebuffer is already initialized and shadow buffer is ready */
if(FrameBufferData.Initialized == FALSE || ScreenShadowBuffer == NULLPTR)
{
/* Unable to operate on non-initialized frame buffer */
return;
}
/* Flush RAM to VRAM */
RTL::Memory::CopyMemory(FrameBufferData.Address,
ScreenShadowBuffer,
FrameBufferData.Pitch * FrameBufferData.Height);
}
/**
* Flushes a specific rectangular region from the Shadow Buffer to the visible FrameBuffer (VRAM).
*
* @param Left
* Supplies the left pixel coordinate of the region to update.
*
* @param Top
* Supplies the top pixel coordinate of the region to update.
*
* @param Right
* Supplies the right pixel coordinate of the region to update.
*
* @param Bottom
* Supplies the bottom pixel coordinate of the region to update.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
HL::FrameBuffer::UpdateScreenRegion(IN ULONG Left,
IN ULONG Top,
IN ULONG Right,
IN ULONG Bottom)
{
ULONG Line, LineBytes;
PCHAR Source, Destination;
/* Make sure framebuffer is already initialized and shadow buffer is ready */
if(FrameBufferData.Initialized == FALSE || ScreenShadowBuffer == NULLPTR)
{
/* Unable to operate on non-initialized frame buffer */
return;
}
/* Make sure parameters are valid to prevent memory corruption */
if(Left >= Right || Top >= Bottom || Right > FrameBufferData.Width || Bottom > FrameBufferData.Height)
{
/* Invalid region coordinates provided */
return;
}
/* Calculate the width of the region */
LineBytes = (Right - Left) * FrameBufferData.BytesPerPixel;
/* Copy the specified region line by line */
for(Line = Top; Line < Bottom; Line++)
{
/* Calculate the source address in the shadow buffer */
Source = (PCHAR)ScreenShadowBuffer +
(Line * FrameBufferData.Pitch) +
(Left * FrameBufferData.BytesPerPixel);
/* Calculate the destination address in the VRAM */
Destination = (PCHAR)FrameBufferData.Address +
(Line * FrameBufferData.Pitch) +
(Left * FrameBufferData.BytesPerPixel);
/* Flush RAM to VRAM */
RTL::Memory::CopyMemory(Destination, Source, LineBytes);
}
}

View File

@@ -19,11 +19,13 @@ namespace HL
{
private:
STATIC HL_FRAMEBUFFER_DATA FrameBufferData;
STATIC PVOID ScreenShadowBuffer;
STATIC HL_SCROLL_REGION_DATA ScrollRegionData;
public:
STATIC XTAPI VOID ClearScreen(IN ULONG Color);
STATIC XTCDECL XTSTATUS DisplayCharacter(IN WCHAR Character);
STATIC XTAPI XTSTATUS EnableShadowBuffer(VOID);
STATIC XTAPI VOID GetFrameBufferResolution(OUT PULONG Width,
OUT PULONG Height);
STATIC XTAPI XTSTATUS InitializeFrameBuffer(VOID);
@@ -32,7 +34,11 @@ namespace HL
IN ULONG Right,
IN ULONG Bottom,
IN ULONG FontColor);
STATIC XTAPI VOID UpdateScreen(VOID);
STATIC XTAPI VOID UpdateScreenRegion(IN ULONG Left,
IN ULONG Top,
IN ULONG Right,
IN ULONG Bottom);
private:
STATIC XTAPI VOID DrawCharacter(IN ULONG PositionX,

View File

@@ -118,6 +118,9 @@ KE::KernelInit::StartKernel(VOID)
/* Initialize Memory Manager */
MM::Manager::InitializeMemoryManager();
/* Enable shadow buffer for framebuffer */
HL::FrameBuffer::EnableShadowBuffer();
/* Enter infinite loop */
DebugPrint(L"KernelInit::StartKernel() finished. Entering infinite loop.\n");
KE::Crash::HaltSystem();

View File

@@ -118,6 +118,9 @@ KE::KernelInit::StartKernel(VOID)
/* Initialize Memory Manager */
MM::Manager::InitializeMemoryManager();
/* Enable shadow buffer for framebuffer */
HL::FrameBuffer::EnableShadowBuffer();
/* Enter infinite loop */
DebugPrint(L"KernelInit::StartKernel() finished. Entering infinite loop.\n");
KE::Crash::HaltSystem();