From 51287eb1a0bc46fafbcfd9a52da78cc69e86febc Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 29 Oct 2026 00:57:00 +0800 Subject: [PATCH] perf(explorer): per-frame window double buffering and present-aware FLUSH --- Apps/Explorer/desk_server.vp | 48 +++++++++++++++++++++++++++--------- vpsdk/vpui.vp | 2 +- vpsdk/window.vp | 4 +-- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Apps/Explorer/desk_server.vp b/Apps/Explorer/desk_server.vp index bad5281..4a4adb0 100644 --- a/Apps/Explorer/desk_server.vp +++ b/Apps/Explorer/desk_server.vp @@ -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) diff --git a/vpsdk/vpui.vp b/vpsdk/vpui.vp index f0f4baf..4a20e6d 100644 --- a/vpsdk/vpui.vp +++ b/vpsdk/vpui.vp @@ -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) diff --git a/vpsdk/window.vp b/vpsdk/window.vp index a24aa8f..5a39576 100644 --- a/vpsdk/window.vp +++ b/vpsdk/window.vp @@ -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