Files
ViperOS/Apps/Scene3D/main.vp
T
TermiNexus bc089a0c69 feat(diag): 用户态窗口渲染链路临时打点
沿 Scene3D → FBCanvas → vpui.redraw → window.flush → explorer 合成器逐段打点,
用于定位窗口内容不上屏停在哪一环:

- Explorer:fb 哨兵像素回读、建窗槽位回读、首个 FLUSH、首帧 back/fb/任务栏像素、
  每 240 帧统计槽位/最上层窗口矩形/中心像素/共享缓冲首像素
- vpui:首次 redraw(缓冲地址尺寸)、st1~st4 分段(malloc/铺底/控件/拷回)、
  首次 flush、FBCanvas 首绘的源缓冲与像素
- Scene3D:主线程预渲染 3 帧(非清屏像素计数、三角形命中/调用数)、渲染线程
  入口与每 60 轮采样;渲染线程参数类型改为 rawptr

以上均为临时诊断,定位完成后应移除。
2026-10-26 22:41:32 +08:00

143 lines
5.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import vpsdk.window as window
import vpsdk.process as process
import vpsdk.vpui as vpui
import vpsdk.devfs as devfs
import string
from stdint import *
import viperlib
import gfx
define SCENE_W = 640
define SCENE_H = 360
_zbuf_mem: [SCENE_W * SCENE_H * 8]uint8 = []
_fb_mem: [SCENE_W * SCENE_H]uint32 = []
_root_ptr: vpui.Tk*
_canvas_ptr: vpui.FBCanvas*
_s3d_ptr: gfx.Scene3D*
_time_val: float = float(0.0)
# 渲染线程的 tid / 已退出标记:主线程必须等它退完才能 process.exit。
# 主线程栈上有 root/canvas/s3d,渲染线程还在引用;process.exit 会立刻回收主线程用户栈,
# 先退主线程 ⇒ 渲染线程在已解映射的栈上取数 ⇒ #PF。
_render_tid: int = -1
_render_done: int = 0
# 时间回绕周期 = 4π:Render 里 time*1 / time*2 / time*1.5 的 sin/cos 参数整体平移 2π 的整数倍
# ⇒ 动画严格连续;同时让相位半径永远很小,避免 float32 精度随运行时间退化
# (x 变大后相邻采样点的相位差被量化 ⇒ 曲线塌成一条平线、热力图颜色失去层次)。
define TIME_WRAP = float(12.566370614359172)
def _dbg(msg: const str):
# 【诊断·临时】与 explorer 同路径的串口输出。app 在建窗失败时会**静默退出**
# (Tk.mainloop 的循环条件是 win_id 有效,win_id=-1 时一次都不循环),所以必须分步打点。
devfs.serial_puts(msg)
def _render_thread(arg: rawptr):
global _time_val, _render_done
root: vpui.Tk* = _root_ptr
canvas: vpui.FBCanvas* = _canvas_ptr
s3d: gfx.Scene3D* = _s3d_ptr
# 【诊断·临时】线程是否真的起来了、参数是否有效
_rb: [160]char
viperlib.snprintf(&_rb, 160, "[s3d] rt enter root=0x%lx s3d=0x%lx active=%d\n",
uint64(root), uint64(s3d), root.winfo_exists())
_dbg(&_rb)
_rn: int = 0
while root.winfo_exists():
s3d.Render(_time_val)
_time_val += float(0.02)
if _time_val >= TIME_WRAP: _time_val = _time_val - TIME_WRAP
root.update()
# 【诊断·临时】每 60 轮打一次:证明渲染循环在跑,并采样画布像素(Clear 色 = 0x0F0F19)
_rn += 1
if (_rn % 60) == 0:
_pb: [176]char
viperlib.snprintf(&_pb, 176, "[s3d] rt n=%d t100=%d fb0=0x%x axis=0x%x\n",
_rn, int(_time_val * float(100.0)),
_fb_mem[0], _fb_mem[(SCENE_H - 150) * SCENE_W + (SCENE_W / 2)])
_dbg(&_pb)
process.yield_cpu()
_dbg("[s3d] rt exit loop\n")
_render_done = 1
# 窗口已被 explorer 回收:显式 THREAD_EXIT,绝不 return —— 用户线程的 return 会落到
# 出口跳板(旧调用号 34,而 THREAD_EXIT=60)⇒ Ring3 `jmp $` 空转、全机不再调度。
process.thread_exit()
@__attribute__(section(".text.startup"), aligned(16))
export def _start() -> int:
_dbg("[s3d] start\n")
root: vpui.Tk = vpui.Tk()
_dbg("[s3d] tk ok\n")
root.title("3D Scene")
root.set_style(window.STYLE_WIN11)
root.bg_color = uint32(0x0F0F19)
master: vpui.Tk* = &root
canvas: vpui.FBCanvas = vpui.FBCanvas(master, SCENE_W, SCENE_H)
_dbg("[s3d] canvas ok\n")
root.pack(0, vpui.PACK_TOP, vpui.FILL_BOTH, 1, 0, 0)
zbuf: float* = &_zbuf_mem
string.memset(zbuf, 0, SCENE_W * SCENE_H * 8)
fb: UINT32PTR = &_fb_mem
string.memset(fb, 0, SCENE_W * SCENE_H * 4)
canvas.set_fb(fb, SCENE_W, SCENE_H)
s3d: gfx.Scene3D = gfx.Scene3D(fb, zbuf, SCENE_W, SCENE_H)
_dbg("[s3d] scene ok\n")
root.geometry(100, 30, SCENE_W, SCENE_H + 32)
_wbuf: [128]char
viperlib.snprintf(&_wbuf, 128, "[s3d] geometry win_id=%d buf=0x%lx\n",
root.win_id, uint64(root.buf))
_dbg(&_wbuf)
global _root_ptr, _canvas_ptr, _s3d_ptr, _render_tid
_root_ptr = &root
_canvas_ptr = &canvas
_s3d_ptr = &s3d
# 【诊断·临时】主线程先直推 3 帧:把"管道"和"线程调度"对半切开。
# 能看见这几帧 ⇒ 渲染→FBCanvas→合成 全通,问题只在 thread_create / 调度;
# 仍然全空 ⇒ 管道里有断点,与线程无关。
global _time_val
_dbg("[s3d] pre-render 3 frames (main thread)\n")
for _pi in range(3):
s3d.Render(_time_val)
_time_val += float(0.02)
root.update()
# 非清屏像素计数:>0 说明确实画出了东西;axis 取 Render 里那条**必画**的横轴
# (DrawLine2D(50, rh-150, rw-50, rh-150),y=210,颜色 0x505050)。
_pre_nz: int = 0
_pre_i: int = 0
while _pre_i < SCENE_W * SCENE_H:
if _fb_mem[_pre_i] != uint32(0x0F0F19): _pre_nz += 1
_pre_i += 1
_pbb: [192]char
viperlib.snprintf(&_pbb, 192, "[s3d] pre %d fb0=0x%x axis=0x%x cube=0x%x nz=%d tri=%d/%d\n",
_pi, _fb_mem[0],
_fb_mem[(SCENE_H - 150) * SCENE_W + (SCENE_W / 2)],
_fb_mem[134 * SCENE_W + 254], _pre_nz,
gfx.DbgTriHits(), gfx.DbgTriCalls())
_dbg(&_pbb)
_render_tid = process.thread_create(_render_thread, None)
_dbg("[s3d] render thread created\n")
root.mainloop()
_dbg("[s3d] mainloop returned\n")
# 窗口已关闭:先等渲染线程退出,才允许回收主线程用户栈(root/canvas/s3d 都在其上)。
# 渲染线程每轮都会 yield 且只差一次循环判定,正常情况下几百次切换内即退出;
# 设上限只是防止它已异常终止却没置标记时把主线程永远卡在这里。
if _render_tid > 0:
wait_n: int = 0
while _render_done == 0 and wait_n < 10000:
wait_n += 1
process.yield_cpu()
process.exit(0)
return 0