perf(explorer): per-frame window double buffering and present-aware FLUSH

This commit is contained in:
TermiNexus committed 2026-10-29 00:57:00 +08:00
1 parent 6f856992da
commit 51287eb1a0
3 files changed
+40 -14

No files matched your search

+37 -11
View File
@@ -854,6 +854,9 @@ class DeskServer:
cap_px: int = w * content_h
buf_off: uint32 = desk.desk_arena_alloc(self.shm, uint32(cap_px) * uint32(4))
if buf_off == uint32(0): return -1
# 第二块内容缓冲:应用始终画在非显示缓冲,FLUSH 后交换。
buf2_off: uint32 = desk.desk_arena_alloc(self.shm, uint32(cap_px) * uint32(4))
if buf2_off == uint32(0): buf2_off = buf_off
s.used = 1
s.pid = pid
s.active = 1
@@ -873,18 +876,22 @@ class DeskServer:
s.fb_cap = uint32(cap_px)
s.fb_w = uint32(w)
s.fb_h = uint32(content_h)
self.vis_off[i] = buf_off
self.vis_off[i] = buf2_off
self.vis_cap[i] = uint32(cap_px)
self.vis_w[i] = w
self.vis_h[i] = content_h
self.spare_off[i] = uint32(0)
self.spare_cap[i] = uint32(0)
string.memset(&s.title, 0, desk.DESK_TITLE_MAX)
# 像素缓冲先清成窗口底色,避免 app 还没画就露出垃圾
# 两块缓冲都清成窗口底色,避免 app 还没画就露出垃圾
buf: uint32* = uint32*(self.shm + uint64(buf_off))
col: uint32 = self.rgb(uint32(30), uint32(30), uint32(50))
for k in range(cap_px):
buf[k] = col
if buf2_off != buf_off:
buf2: uint32* = uint32*(self.shm + uint64(buf2_off))
for k2 in range(cap_px):
buf2[k2] = col
s.z_order = self.next_z
self.next_z += 1
self.focused = i
@@ -1998,16 +2005,35 @@ class DeskServer:
if self.hit_test(self.cx, self.cy) == win:
self._mark_cursor_band()
elif ty == uint32(desk.DESK_REQ_FLUSH):
# 应用已经按 s.fb_w/fb_h 画完一帧:此刻才把"正在显示的内容"切到
# 新缓冲。旧的 vis 缓冲留作下一次 resize 的备用。
# present=0:PING/保活,不交换缓冲,避免显示未绘制的新缓冲。
if m.a == 0:
return
old_off: uint32 = self.vis_off[win]
if old_off != s.fb_off:
self.spare_off[win] = old_off
self.spare_cap[win] = self.vis_cap[win]
self.vis_off[win] = s.fb_off
self.vis_cap[win] = s.fb_cap
self.vis_w[win] = int(s.fb_w)
self.vis_h[win] = int(s.fb_h)
old_cap: uint32 = self.vis_cap[win]
need: int = int(s.fb_w) * int(s.fb_h)
if s.fb_off != old_off:
# 本帧画在新的 offscreen 缓冲里:切到它,旧 vis 作为下一帧目标。
self.vis_off[win] = s.fb_off
self.vis_cap[win] = s.fb_cap
self.vis_w[win] = int(s.fb_w)
self.vis_h[win] = int(s.fb_h)
if old_off >= uint32(desk.DESK_OFF_ARENA) and int(old_cap) >= need:
s.fb_off = old_off
s.fb_cap = old_cap
else:
nw: uint32 = desk.desk_arena_alloc(self.shm, uint32(need * 4))
if nw != uint32(0):
s.fb_off = nw
s.fb_cap = uint32(need)
else:
s.fb_off = old_off
s.fb_cap = old_cap
else:
# 首帧或单缓冲回退:vis 已经是当前内容,为下一帧准备第二块。
nw2: uint32 = desk.desk_arena_alloc(self.shm, uint32(need * 4))
if nw2 != uint32(0):
s.fb_off = nw2
s.fb_cap = uint32(need)
# 最小化窗口仍在后台绘制/FLUSH:不要置脏,否则合成器每帧都要重画背景。
if s.minimized == 0:
self.mark(s.uy, s.h)
+1 -1
View File
@@ -2050,7 +2050,7 @@ virtual class Tk(Wm, Misc):
need_redraw: int = 0
etype: uint32 = evt.type
if etype == window.EVENT_TYPE_PING:
window.flush(self.win_id)
window.flush(self.win_id, 0)
elif etype == window.EVENT_TYPE_RESIZE:
self.buf = window.get_winbuf(self.win_id)
sz: int = window.get_win_size(self.win_id)
+2 -2
View File
@@ -183,13 +183,13 @@ def get_win_size(win_id: int) -> int:
if s.used == 0: return 0
return (int(s.fb_w) << 16) | (int(s.fb_h) & 0xFFFF)
def flush(win_id: int):
def flush(win_id: int, present: int = 1):
# 槽位已被 explorer 回收(用户点了关闭):直接安全返回,绝不再发合成请求。
if _connect() != 0: return
if win_id < 0 or win_id >= desk.DESK_MAX_WIN: return
s: desk.WinSlot* = _slot(win_id)
if s.used == 0: return
_send(uint32(desk.DESK_REQ_FLUSH), win_id, 0, 0, 0, 0, None)
_send(uint32(desk.DESK_REQ_FLUSH), win_id, present, 0, 0, 0, None)
def text(win_id: int, x: int, y: int, color: uint32, s: const str) -> int:
if _connect() != 0: return -1