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:
t3hn3rd committed 2026-02-28 15:38:11 +00:00
1 parent 6f50b053b7
commit be4b482bde
4 files changed
+42 -16

No files matched your search

+1 -2
View File
@@ -32,8 +32,7 @@ implementation
function allocateBackBuffer(Width : uint32; Height : uint32; BitsPerPixel : uint8) : uint64; function allocateBackBuffer(Width : uint32; Height : uint32; BitsPerPixel : uint8) : uint64;
begin begin
tracer.push_trace('doublebuffer.allocateBackBuffer.enter'); tracer.push_trace('doublebuffer.allocateBackBuffer.enter');
//This doesn't currently work... Needs a rework of lmemorymanager allocateBackBuffer:= uint64(klalloc((Width * Height * BitsPerPixel) div 8));
allocateBackBuffer:= uint64(klalloc((Width * Height) * BitsPerPixel));
tracer.push_trace('doublebuffer.allocateBackBuffer.exit'); tracer.push_trace('doublebuffer.allocateBackBuffer.exit');
end; end;
+33 -6
View File
@@ -1725,7 +1725,8 @@ const
LV_BUF_LINES = 120; LV_BUF_LINES = 120;
var var
lv_buf1: array[0..1600*LV_BUF_LINES-1] of lv_color_t; lv_buf1: pointer;
lv_buf1_size: uint32;
{ ============================================================ { ============================================================
Color helper Color helper
@@ -1740,25 +1741,47 @@ end;
{ ============================================================ { ============================================================
Flush callback — copies LVGL render buffer to video back buffer 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; procedure lvgl_flush_cb(disp: Plv_display; area: Plv_area; color_p: Plv_color); cdecl;
var var
y, x, area_w: sint32; y, x, area_w: sint32;
src: puint32; src: puint32;
dst: puint32; dst32: puint32;
dst16: puint16;
fb_w: uint32; fb_w: uint32;
bpp: uint8;
pixel: uint32;
r, g, b: uint8;
begin begin
fb_w := video.backBufferWidth; fb_w := video.backBufferWidth;
bpp := video.backBufferBpp;
area_w := (area^.x2 - area^.x1) + 1; area_w := (area^.x2 - area^.x1) + 1;
src := puint32(color_p); src := puint32(color_p);
if bpp >= 32 then begin
{ 32bpp: direct copy, 4 bytes per pixel }
for y := area^.y1 to area^.y2 do begin for y := area^.y1 to area^.y2 do begin
dst := puint32(video.backBufferLocation + uint32((y * sint32(fb_w) + area^.x1) * 4)); dst32 := puint32(video.backBufferLocation + uint32((y * sint32(fb_w) + area^.x1) * 4));
for x := 0 to area_w - 1 do begin for x := 0 to area_w - 1 do begin
dst[x] := src[x]; dst32[x] := src[x];
end; end;
src := puint32(uint32(src) + uint32(area_w * 4)); src := puint32(uint32(src) + uint32(area_w * 4));
end; 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;
end;
lv_display_flush_ready(disp); lv_display_flush_ready(disp);
end; end;
@@ -1852,11 +1875,15 @@ begin
{ Register log callback } { Register log callback }
lv_log_register_print_cb(@lvgl_log_cb); 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 } { Create display }
disp := lv_display_create(sint32(screen_w), sint32(screen_h)); disp := lv_display_create(sint32(screen_w), sint32(screen_h));
lv_display_set_flush_cb(disp, @lvgl_flush_cb); lv_display_set_flush_cb(disp, @lvgl_flush_cb);
lv_display_set_buffers(disp, @lv_buf1[0], nil, lv_display_set_buffers(disp, lv_buf1, nil,
SizeOf(lv_buf1), LV_DISPLAY_RENDER_MODE_PARTIAL); lv_buf1_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
{ Create mouse input device with read callback } { Create mouse input device with read callback }
mouse_indev := lv_indev_create; mouse_indev := lv_indev_create;
+7 -5
View File
@@ -32,22 +32,24 @@ implementation
uses uses
VESA8, VESA16, VESA24, VESA32; VESA8, VESA16, VESA24, VESA32;
procedure allocateVESAFrameBuffer(Address : uint32; Width : uint32; Height : uint32); procedure allocateVESAFrameBuffer(Address : uint32; Width : uint32; Height : uint32; BitsPerPixel : uint8);
var var
LowerAddress, UpperAddress : uint32; LowerAddress, UpperAddress : uint32;
Block : uint32; Block : uint32;
FrameBufferSize : uint32;
begin begin
tracer.push_trace('VESA.allocateFrameBuffer.enter'); tracer.push_trace('VESA.allocateFrameBuffer.enter');
FrameBufferSize := (Width * Height * BitsPerPixel) div 8;
LowerAddress:= ((Address) SHR 22)-1; 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 For Block:=LowerAddress to UpperAddress do begin
kpalloc(Block SHL 22); kpalloc(Block SHL 22);
end; end;
tracer.push_trace('VESA.allocateFrameBuffer.exit'); tracer.push_trace('VESA.allocateFrameBuffer.exit');
end; 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 begin
tracer.push_trace('VESA.initVESAFrameBuffer.enter'); tracer.push_trace('VESA.initVESAFrameBuffer.enter');
if not(VideoBuffer^.Initialized) then begin if not(VideoBuffer^.Initialized) then begin
@@ -55,7 +57,7 @@ begin
VideoBuffer^.BitsPerPixel:= BitsPerPixel; VideoBuffer^.BitsPerPixel:= BitsPerPixel;
VideoBuffer^.Width:= Width; VideoBuffer^.Width:= Width;
VideoBuffer^.Height:= Height; VideoBuffer^.Height:= Height;
allocateVESAFrameBuffer(VideoBuffer^.Location, VideoBuffer^.Width, VideoBuffer^.Height); allocateVESAFrameBuffer(VideoBuffer^.Location, VideoBuffer^.Width, VideoBuffer^.Height, BitsPerPixel);
if VideoBuffer^.Location <> 0 then if VideoBuffer^.Location <> 0 then
VideoBuffer^.Initialized:= True; VideoBuffer^.Initialized:= True;
end; end;
@@ -65,7 +67,7 @@ end;
function enable(VideoInterface : PVideoInterface) : boolean; function enable(VideoInterface : PVideoInterface) : boolean;
begin begin
tracer.push_trace('VESA.enable.enter'); 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 case (VideoInterface^.FrontBuffer.BitsPerPixel) of
08:VESA8.init(@VideoInterface^.DrawRoutines); 08:VESA8.init(@VideoInterface^.DrawRoutines);
16:VESA16.init(@VideoInterface^.DrawRoutines); 16:VESA16.init(@VideoInterface^.DrawRoutines);
+1 -3
View File
@@ -123,8 +123,6 @@ begin
multibootinfo:= mbinfo; multibootinfo:= mbinfo;
multibootmagic:= mbmagic; multibootmagic:= mbmagic;
//video.init();
{ Ensure tracer is frozen } { Ensure tracer is frozen }
tracer.freeze(); tracer.freeze();
@@ -181,7 +179,7 @@ begin
scheduler.init(); scheduler.init();
{ Console Init } { Console Init }
console.init(); //console.init();
{ CPUID } { CPUID }
console.outputln('CPU', 'Init begin'); console.outputln('CPU', 'Init begin');