fix: LVGL rendering for 16bpp framebuffers (HyperV compatibility)
continuous-integration/drone/push Build is failing

The LVGL flush callback assumed 32bpp (4-byte stride, direct pixel
copy), causing corrupted output on hypervisors that fall back to
16bpp modes (e.g. HyperV at 1600x1200x16).

- lvgl.pas: Make lvgl_flush_cb BPP-aware; add ARGB8888->RGB565
  conversion path for 16bpp, keep direct copy for 32bpp. Replace
  static lv_buf1 array (hardcoded 1600px wide) with kalloc'd buffer
  sized to actual screen width.
- doublebuffer.pas: Fix allocateBackBuffer size from (W*H*BPP) to
  (W*H*BPP) div 8 to get correct byte count.
- vesa.pas: Account for BPP in allocateVESAFrameBuffer page range
  calculation to avoid under-mapping at higher BPP/resolutions.
- Removed the initialization of console in kernel.pas as this will
  cause issues on some systems that don't expect to need to allocate the
  textmode buffer.
This commit is contained in:
2026-02-28 15:38:11 +00:00
parent 6f50b053b7
commit be4b482bde
4 changed files with 45 additions and 19 deletions
+1 -2
View File
@@ -32,8 +32,7 @@ implementation
function allocateBackBuffer(Width : uint32; Height : uint32; BitsPerPixel : uint8) : uint64;
begin
tracer.push_trace('doublebuffer.allocateBackBuffer.enter');
//This doesn't currently work... Needs a rework of lmemorymanager
allocateBackBuffer:= uint64(klalloc((Width * Height) * BitsPerPixel));
allocateBackBuffer:= uint64(klalloc((Width * Height * BitsPerPixel) div 8));
tracer.push_trace('doublebuffer.allocateBackBuffer.exit');
end;
+36 -9
View File
@@ -1725,7 +1725,8 @@ const
LV_BUF_LINES = 120;
var
lv_buf1: array[0..1600*LV_BUF_LINES-1] of lv_color_t;
lv_buf1: pointer;
lv_buf1_size: uint32;
{ ============================================================
Color helper
@@ -1740,24 +1741,46 @@ end;
{ ============================================================
Flush callback — copies LVGL render buffer to video back buffer
Handles 32bpp (direct copy) and 16bpp (ARGB8888 -> RGB565).
============================================================ }
procedure lvgl_flush_cb(disp: Plv_display; area: Plv_area; color_p: Plv_color); cdecl;
var
y, x, area_w: sint32;
src: puint32;
dst: puint32;
dst32: puint32;
dst16: puint16;
fb_w: uint32;
bpp: uint8;
pixel: uint32;
r, g, b: uint8;
begin
fb_w := video.backBufferWidth;
bpp := video.backBufferBpp;
area_w := (area^.x2 - area^.x1) + 1;
src := puint32(color_p);
for y := area^.y1 to area^.y2 do begin
dst := puint32(video.backBufferLocation + uint32((y * sint32(fb_w) + area^.x1) * 4));
for x := 0 to area_w - 1 do begin
dst[x] := src[x];
if bpp >= 32 then begin
{ 32bpp: direct copy, 4 bytes per pixel }
for y := area^.y1 to area^.y2 do begin
dst32 := puint32(video.backBufferLocation + uint32((y * sint32(fb_w) + area^.x1) * 4));
for x := 0 to area_w - 1 do begin
dst32[x] := src[x];
end;
src := puint32(uint32(src) + uint32(area_w * 4));
end;
end else if bpp = 16 then begin
{ 16bpp: convert ARGB8888 -> RGB565, 2 bytes per pixel }
for y := area^.y1 to area^.y2 do begin
dst16 := puint16(video.backBufferLocation + uint32((y * sint32(fb_w) + area^.x1) * 2));
for x := 0 to area_w - 1 do begin
pixel := src[x];
b := pixel and $FF;
g := (pixel shr 8) and $FF;
r := (pixel shr 16) and $FF;
dst16[x] := uint16(((uint16(r) shr 3) shl 11) or ((uint16(g) shr 2) shl 5) or (uint16(b) shr 3));
end;
src := puint32(uint32(src) + uint32(area_w * 4));
end;
src := puint32(uint32(src) + uint32(area_w * 4));
end;
lv_display_flush_ready(disp);
@@ -1852,11 +1875,15 @@ begin
{ Register log callback }
lv_log_register_print_cb(@lvgl_log_cb);
{ Allocate LVGL render buffer dynamically (1/10th of screen) }
lv_buf1_size := screen_w * LV_BUF_LINES * SizeOf(lv_color_t);
lv_buf1 := pointer(kalloc(lv_buf1_size));
{ Create display }
disp := lv_display_create(sint32(screen_w), sint32(screen_h));
lv_display_set_flush_cb(disp, @lvgl_flush_cb);
lv_display_set_buffers(disp, @lv_buf1[0], nil,
SizeOf(lv_buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);
lv_display_set_buffers(disp, lv_buf1, nil,
lv_buf1_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
{ Create mouse input device with read callback }
mouse_indev := lv_indev_create;
+7 -5
View File
@@ -32,22 +32,24 @@ implementation
uses
VESA8, VESA16, VESA24, VESA32;
procedure allocateVESAFrameBuffer(Address : uint32; Width : uint32; Height : uint32);
procedure allocateVESAFrameBuffer(Address : uint32; Width : uint32; Height : uint32; BitsPerPixel : uint8);
var
LowerAddress, UpperAddress : uint32;
Block : uint32;
FrameBufferSize : uint32;
begin
tracer.push_trace('VESA.allocateFrameBuffer.enter');
FrameBufferSize := (Width * Height * BitsPerPixel) div 8;
LowerAddress:= ((Address) SHR 22)-1;
UpperAddress:= ((Address + (Width * Height)) SHR 22)+1;
UpperAddress:= ((Address + FrameBufferSize) SHR 22)+1;
For Block:=LowerAddress to UpperAddress do begin
kpalloc(Block SHL 22);
end;
tracer.push_trace('VESA.allocateFrameBuffer.exit');
end;
procedure initVESAFrameBuffer(VideoBuffer : PVideoBuffer; Location : uint64; Width : uint32; Height : uint32; BitsPerPixel : uint8);
procedure initVESAFrameBuffer(VideoBuffer : PVideoBuffer; Location : uint64; Width : uint32; Height : uint32; BitsPerPixel : uint8; Pitch : uint32);
begin
tracer.push_trace('VESA.initVESAFrameBuffer.enter');
if not(VideoBuffer^.Initialized) then begin
@@ -55,7 +57,7 @@ begin
VideoBuffer^.BitsPerPixel:= BitsPerPixel;
VideoBuffer^.Width:= Width;
VideoBuffer^.Height:= Height;
allocateVESAFrameBuffer(VideoBuffer^.Location, VideoBuffer^.Width, VideoBuffer^.Height);
allocateVESAFrameBuffer(VideoBuffer^.Location, VideoBuffer^.Width, VideoBuffer^.Height, BitsPerPixel);
if VideoBuffer^.Location <> 0 then
VideoBuffer^.Initialized:= True;
end;
@@ -65,7 +67,7 @@ end;
function enable(VideoInterface : PVideoInterface) : boolean;
begin
tracer.push_trace('VESA.enable.enter');
initVESAFrameBuffer(@VideoInterface^.FrontBuffer, multiboot.multibootinfo^.framebuffer_addr, multiboot.multibootinfo^.framebuffer_width, multiboot.multibootinfo^.framebuffer_height, multiboot.multibootinfo^.framebuffer_bpp);
initVESAFrameBuffer(@VideoInterface^.FrontBuffer, multiboot.multibootinfo^.framebuffer_addr, multiboot.multibootinfo^.framebuffer_width, multiboot.multibootinfo^.framebuffer_height, multiboot.multibootinfo^.framebuffer_bpp, multiboot.multibootinfo^.framebuffer_pitch);
case (VideoInterface^.FrontBuffer.BitsPerPixel) of
08:VESA8.init(@VideoInterface^.DrawRoutines);
16:VESA16.init(@VideoInterface^.DrawRoutines);
+1 -3
View File
@@ -123,8 +123,6 @@ begin
multibootinfo:= mbinfo;
multibootmagic:= mbmagic;
//video.init();
{ Ensure tracer is frozen }
tracer.freeze();
@@ -181,7 +179,7 @@ begin
scheduler.init();
{ Console Init }
console.init();
//console.init();
{ CPUID }
console.outputln('CPU', 'Init begin');