diff --git a/xtoskrnl/hl/data.cc b/xtoskrnl/hl/data.cc index 970fcfc..4a2b899 100644 --- a/xtoskrnl/hl/data.cc +++ b/xtoskrnl/hl/data.cc @@ -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; diff --git a/xtoskrnl/hl/fbdev.cc b/xtoskrnl/hl/fbdev.cc index 5b7d42d..4cc5790 100644 --- a/xtoskrnl/hl/fbdev.cc +++ b/xtoskrnl/hl/fbdev.cc @@ -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) { @@ -91,15 +104,20 @@ HL::FrameBuffer::DisplayCharacter(IN WCHAR Character) case L'\t': /* Move cursor to the next tab stop */ ScrollRegionData.CursorX += (8 - (ScrollRegionData.CursorX - ScrollRegionData.Left) / FbFont->Width % 8) * FbFont->Width; - if (ScrollRegionData.CursorX >= ScrollRegionData.Right) + if(ScrollRegionData.CursorX >= ScrollRegionData.Right) { ScrollRegionData.CursorX = ScrollRegionData.Left; ScrollRegionData.CursorY += FbFont->Height; } 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); - } - - /* Clear the last text line */ - for(Line = ScrollRegionData.Bottom - FbFont->Height; Line < ScrollRegionData.Bottom; Line++) - { - /* 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 */ - for(PositionX = 0; PositionX < (ScrollRegionData.Right - ScrollRegionData.Left); PositionX++) + /* Check if the current line needs to be copied from below or cleared */ + if(Line < ScrollRegionData.Bottom - FbFont->Height) { - Pixel[PositionX] = ScrollRegionData.BackgroundColor; + /* Copy the line from below */ + Source = Destination + (FbFont->Height * FrameBufferData.Pitch); + RTL::Memory::CopyMemory(Destination, Source, LineBytes); + } + else + { + /* 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); + } } diff --git a/xtoskrnl/includes/hl/fbdev.hh b/xtoskrnl/includes/hl/fbdev.hh index f6b7c7a..4760738 100644 --- a/xtoskrnl/includes/hl/fbdev.hh +++ b/xtoskrnl/includes/hl/fbdev.hh @@ -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, diff --git a/xtoskrnl/ke/amd64/krnlinit.cc b/xtoskrnl/ke/amd64/krnlinit.cc index 03a9895..d19742b 100644 --- a/xtoskrnl/ke/amd64/krnlinit.cc +++ b/xtoskrnl/ke/amd64/krnlinit.cc @@ -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(); diff --git a/xtoskrnl/ke/i686/krnlinit.cc b/xtoskrnl/ke/i686/krnlinit.cc index 7a9a913..955d618 100644 --- a/xtoskrnl/ke/i686/krnlinit.cc +++ b/xtoskrnl/ke/i686/krnlinit.cc @@ -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();