perf(explorer): 合成改画后备缓冲,渲染末尾只提交脏行到 framebuffer

消除撕裂:原来所有绘制直接写真实 framebuffer,与显示器扫描并发 ⇒ 撕裂/闪烁;而用户态无法做垂直同步(QEMU/OVMF 的 GOP 线性帧缓冲没有 VGA 回扫信号,FB_WAIT_VBLANK 实测返回 0 空转)。改法对齐历史内核桌面的做法:DeskServer 增加进程本地后备缓冲 back(与 bg_row 同机制 mmap 匿名页),row_fill/row_blit/draw_char/阴影混合/任务栏 logo 等所有写入点从 self.fb 改到 self.back(back 行距 = fb_w,framebuffer 行距 = pitch_px),render() 先把脏行范围渲染到 back,末尾再把仍脏的行逐行 memcpy 提交到 framebuffer。分配失败时打印并退回直写。同时删掉已完成使命的 vblank iters 诊断打印。
This commit is contained in:
TermiNexus committed 2026-10-16 22:31:35 +08:00
1 parent 3945decc2d
commit 5f863bed71
1 file changed
+29 -18
+29 -18
View File
@@ -11,8 +11,6 @@ import viperlib
# 【诊断·临时】首个鼠标包已收到的标记(0 = 未收到)
_mouse_logged: int = 0
# 首次 vblank 等待的迭代数只打一次(诊断同步是否真的生效;到上限 = 超时)
_vblank_logged: int = 0
# ============================================================================
@@ -78,6 +76,7 @@ class DeskServer:
pitch_px: int
fmt: uint32 # 0 = RGBA, 1 = BGRA
bg_row: uint32* # 一行背景的预构造副本
back: uint32* # 后备缓冲:绘制先落这里,整帧后逐行拷到 fb
# 共享区
shm: uint64
# 输入
@@ -167,6 +166,16 @@ class DeskServer:
col: uint32 = self.rgb(uint32(25), uint32(25), uint32(35))
for i in range(w):
self.bg_row[i] = col
# 后备缓冲:整屏像素,行距 = fb_w(与 framebuffer 的 pitch 无关)
back_va: uint64 = syscall.mmap(0, uint64(w) * uint64(h) * uint64(4),
uint64(syscall.PROT_READ | syscall.PROT_WRITE),
uint64(syscall.MAP_PRIVATE | syscall.MAP_ANONYMOUS),
syscall.MMAP_FD_ANON, 0)
if back_va == 0:
devfs.serial_puts("[explorer] back buffer alloc FAILED\n")
self.back = self.fb # 退化:直写 framebuffer,保留原行为
else:
self.back = uint32*(back_va)
self.mark_all()
# ---------------- 脏行 ----------------
@@ -215,8 +224,8 @@ class DeskServer:
if x + dw > self.fb_w:
dw = self.fb_w - x
if dw <= 0: return
base: int = y * self.pitch_px
string.memset32(rawptr(uint64(self.fb) + uint64((base + x) * 4)), col, uint64(dw))
base: int = y * self.fb_w
string.memset32(rawptr(uint64(self.back) + uint64((base + x) * 4)), col, uint64(dw))
def row_blit(self, x0: int, y: int, src: uint32*, src_off: int, w: int):
"""把 src[src_off .. src_off+w) 拷到屏幕 (x0, y),裁剪到屏幕内。"""
@@ -231,7 +240,7 @@ class DeskServer:
if dx + dw > self.fb_w:
dw = self.fb_w - dx
if dw <= 0: return
dst: uint64 = uint64(self.fb) + uint64(y * self.pitch_px + dx) * uint64(4)
dst: uint64 = uint64(self.back) + uint64(y * self.fb_w + dx) * uint64(4)
srcp: uint64 = uint64(src) + uint64(src_off + sx) * uint64(4)
string.memcpy(rawptr(dst), rawptr(srcp), uint64(dw) * uint64(4))
@@ -295,13 +304,13 @@ class DeskServer:
if ty >= 0 and ty < self.fb_h:
row: uint8 = vpui.font[ci][fy]
if row != uint8(0):
base: int = ty * self.pitch_px
base: int = ty * self.fb_w
fx: int = 0
while fx < vpui.CHAR_W:
if row & (0x80 >> fx):
px: int = x + fx
if px >= 0 and px < self.fb_w:
self.fb[base + px] = col
self.back[base + px] = col
fx += 1
fy += 1
@@ -364,8 +373,8 @@ class DeskServer:
t_inv: int = 256 - t_val
a: int = (sa * t_inv * t_inv) / 65536
if a > 0:
idx: int = yy * self.pitch_px + px
self.fb[idx] = self._blend(self.fb[idx], shadow_col, a)
idx: int = yy * self.fb_w + px
self.back[idx] = self._blend(self.back[idx], shadow_col, a)
px += 1
yy += 1
@@ -450,9 +459,9 @@ class DeskServer:
px: int = logo_x + lx
if px >= 0 and px < self.fb_w:
if d2 <= (lr * lr) / 2:
self.fb[yy * self.pitch_px + px] = lc_core
self.back[yy * self.fb_w + px] = lc_core
else:
self.fb[yy * self.pitch_px + px] = lc_ring
self.back[yy * self.fb_w + px] = lc_ring
lx += 1
yy += 1
# 品牌文字
@@ -666,7 +675,6 @@ class DeskServer:
y += 1
def render(self):
global _vblank_logged
if self.dirty_any == 0: return
y0: int = -1
y1: int = -1
@@ -681,13 +689,16 @@ class DeskServer:
return
# 写屏前等垂直回扫(用户态无 IO 权限,由内核代等);脏行合并成**一段**连续范围
# 一次性渲染 —— 分段渲染会让同一帧的多次写屏跨越多个扫描周期,撕裂更明显。
vw: int = devfs.fb_wait_vblank(self.fb_fd)
if _vblank_logged == 0:
_vblank_logged = 1
_vb: [96]char
viperlib.snprintf(&_vb, 96, "[explorer] vblank iters=%d\n", vw)
devfs.serial_puts(&_vb)
devfs.fb_wait_vblank(self.fb_fd)
self.render_range(y0, y1)
# 提交:整帧已画进 back,只把仍脏的行拷到 framebuffer(fb 行距 = pitch_px)
y = y0
while y < y1:
if self.dirty[y] != 0:
string.memcpy(rawptr(uint64(self.fb) + uint64(y * self.pitch_px) * uint64(4)),
rawptr(uint64(self.back) + uint64(y * self.fb_w) * uint64(4)),
uint64(self.fb_w) * uint64(4))
y += 1
self.clear_dirty()
# ---------------- 事件投递 ----------------