fix(vpui): 窗口改双缓冲,消除合成器读到半成品帧造成的闪烁
根因:Tk.redraw 每帧先 _fill_rect 把整窗填成背景色、再逐控件重画,而这块缓冲就是 explorer 合成时逐行 blit 的共享 arena(render_window_rows 直接从 arena 读)。于是整窗纯背景色、控件还没画的那段窗口期一旦被合成器采到就是一次闪烁;鼠标移动让 explorer 重画窗口所在行时同样会撞上。改法:Tk 增加进程本地后备缓冲(懒分配、窗口变大时重新分配并 free 旧的),整帧画完后一次性 memcpy 进 arena 再 flush,合成器任何时刻读到的都是完整帧。代价是每帧一次全窗拷贝(零拷贝通路只在合成那一段保留)。分配失败不静默:打日志后退回直写 arena。
This commit is contained in:
1 parent
6eb000d01d
commit
d840c7546f
1 file changed
+23
-2
+23
-2
@@ -1,6 +1,7 @@
|
||||
import vpsdk.window as window
|
||||
import vpsdk.process as process
|
||||
import vpsdk.devfs as devfs
|
||||
import vpsdk.stdlib as stdlib
|
||||
import string
|
||||
|
||||
define TITLE_H = 32
|
||||
@@ -1512,6 +1513,8 @@ virtual class Tk(Wm, Misc):
|
||||
resizable_h: int
|
||||
_grid_rows: int
|
||||
_grid_cols: int
|
||||
_bb: uint32* # 后备缓冲:整帧画完再一次性拷进共享 arena,避免合成器读到半成品帧
|
||||
_bb_px: int # 后备缓冲容量(像素数),窗口变大时才重新分配
|
||||
|
||||
def __init__(self):
|
||||
global _g_layout_init
|
||||
@@ -1543,6 +1546,8 @@ virtual class Tk(Wm, Misc):
|
||||
_g_wl_colspan[i] = 1
|
||||
_g_wl_sticky[i] = 0
|
||||
_g_layout_init = 1
|
||||
self._bb = None
|
||||
self._bb_px = 0
|
||||
for j in range(32):
|
||||
self._title[j] = 0
|
||||
|
||||
@@ -1615,11 +1620,27 @@ virtual class Tk(Wm, Misc):
|
||||
self.bh = sz & 0xFFFF
|
||||
if not self.buf: return
|
||||
if self.bw <= 0 or self.bh <= 0: return
|
||||
_fill_rect(self.buf, self.bw, self.bh, 0, 0, self.bw, self.bh, self.bg_color)
|
||||
# 双缓冲:整帧先画在**进程本地**的后备缓冲里,画完一次性拷进共享 arena,再 flush。
|
||||
# 为什么必须这么做:arena 正是 explorer 合成时读的那块内存(render_window_rows
|
||||
# 直接从它逐行 blit 上屏)。若直接往 arena 上画,合成器会在"整窗已填成背景色、
|
||||
# 控件还没画"的中途采样 ⇒ 窗口闪烁;光标移动让 explorer 重画窗口所在行时同样撞上。
|
||||
px: int = self.bw * self.bh
|
||||
if self._bb_px < px:
|
||||
if self._bb: stdlib.free(rawptr(self._bb))
|
||||
self._bb = uint32*(stdlib.malloc(uint64(px * 4)))
|
||||
self._bb_px = px
|
||||
tgt: uint32* = self._bb
|
||||
if not tgt:
|
||||
# 分配失败不静默:打日志后**退回直写 arena**(画面仍能出,只是回到有撕裂的形态)。
|
||||
devfs.serial_puts("[vpui] backbuffer alloc FAILED, drawing straight into arena\n")
|
||||
tgt = self.buf
|
||||
_fill_rect(tgt, self.bw, self.bh, 0, 0, self.bw, self.bh, self.bg_color)
|
||||
for i in range(self.widget_count):
|
||||
w: Widget* = self.widgets[i]
|
||||
if w.visible:
|
||||
w.draw(self.buf, self.bw, self.bh)
|
||||
w.draw(tgt, self.bw, self.bh)
|
||||
if tgt != self.buf:
|
||||
string.memcpy(self.buf, tgt, uint64(px * 4))
|
||||
window.flush(self.win_id)
|
||||
|
||||
def _do_layout(self):
|
||||
|
||||
Reference in new issue
Block a user