feat(fb0): 新增 FB_WAIT_VBLANK ioctl,合成前等垂直回扫
explorer 直接写 framebuffer 会与显示器扫描并发,造成撕裂/闪烁;而它在 Ring3 没有 IO 端口权限,不能像历史内核桌面那样自己 inb 0x3DA。所以在 /dev/fb0 上加 FB_WAIT_VBLANK(0x4601)由内核代等垂直回扫(带超时,无 VGA 时序时不死等),vpsdk 暴露 fb_wait_vblank(),desk_server.render() 写屏前调用一次。
This commit is contained in:
1 parent
89c37cea22
commit
e41701e201
4 files changed
+23
-6
No files matched your search
@@ -62,6 +62,7 @@ _CURSOR_ARROW: [256]uint8 = [
|
||||
class DeskServer:
|
||||
# 屏幕
|
||||
fb: uint32* # /dev/fb0 映射后的线性地址
|
||||
fb_fd: int # /dev/fb0 的 fd(等 vblank 用)
|
||||
fb_w: int
|
||||
fb_h: int
|
||||
pitch_px: int
|
||||
@@ -97,8 +98,9 @@ class DeskServer:
|
||||
return (uint32(255) << 24) | (b << 16) | (g << 8) | r
|
||||
|
||||
def init(self, fb: uint32*, w: int, h: int, pitch: int, fmt: uint32,
|
||||
shm: uint64, kfd: int, mfd: int):
|
||||
shm: uint64, kfd: int, mfd: int, fb_fd: int):
|
||||
self.fb = fb
|
||||
self.fb_fd = fb_fd
|
||||
self.fb_w = w
|
||||
self.fb_h = h
|
||||
self.pitch_px = pitch / 4
|
||||
@@ -355,6 +357,9 @@ class DeskServer:
|
||||
|
||||
def render(self):
|
||||
if self.dirty_any == 0: return
|
||||
# 写屏前等垂直回扫:直接写 framebuffer 会与显示器扫描并发 ⇒ 撕裂/闪烁。
|
||||
# 用户态没有 IO 权限(不能 inb 0x3DA),由内核代等。
|
||||
devfs.fb_wait_vblank(self.fb_fd)
|
||||
y: int = 0
|
||||
while y < self.fb_h:
|
||||
if self.dirty[y] == 0:
|
||||
|
||||
@@ -87,7 +87,7 @@ export def _start() -> int:
|
||||
|
||||
# 6) 进入窗口服务端主循环(永不返回):请求环 + 输入 + 合成。
|
||||
srv: desk_server.DeskServer = desk_server.DeskServer()
|
||||
srv.init(fb, int(info.width), int(info.height), int(info.pitch), info.format, shm, kfd, mfd)
|
||||
srv.init(fb, int(info.width), int(info.height), int(info.pitch), info.format, shm, kfd, mfd, fb_fd)
|
||||
_dbg("[explorer] desk server running\n")
|
||||
srv.serve()
|
||||
return 0
|
||||
@@ -1,5 +1,6 @@
|
||||
import drivers.serial.uart.serial as serial
|
||||
import string
|
||||
import asm
|
||||
|
||||
|
||||
define MAX_FILE_DESCRIPTORS = 64
|
||||
@@ -487,6 +488,8 @@ def devfs_register_mouse() -> int:
|
||||
# 本节点只记录 BootInfo 里的物理地址与几何,不自己持有 framebuffer 映射。
|
||||
# ============================================================================
|
||||
define FB_GET_INFO = 0x4600
|
||||
# 等一次垂直回扫:用户态(explorer)没有 IO 端口权限,不能自己 inb 0x3DA,由内核代做。
|
||||
define FB_WAIT_VBLANK = 0x4601
|
||||
|
||||
@__attribute__(packed)
|
||||
class FbInfo:
|
||||
@@ -518,6 +521,13 @@ virtual class framebuffer_device(devfs_file):
|
||||
return -1
|
||||
|
||||
def ioctl(self, fd: int, request: int, arg: rawptr) -> int:
|
||||
if request == FB_WAIT_VBLANK:
|
||||
# 等进入垂直回扫期(0x3DA bit3)。带超时:当前若无 VGA 时序不至于死等。
|
||||
t: int = 100000
|
||||
while (asm.inb(0x3DA) & 0x08) != 0 and t > 0: t -= 1
|
||||
t = 100000
|
||||
while (asm.inb(0x3DA) & 0x08) == 0 and t > 0: t -= 1
|
||||
return 0
|
||||
if request != FB_GET_INFO or not arg:
|
||||
return -1
|
||||
info: FbInfo* = FbInfo*(uint64(arg))
|
||||
|
||||
+6
-4
@@ -22,6 +22,7 @@ define DEV_DESK = "/dev/desk"
|
||||
# ioctl 命令与返回结构:必须与 VKernel/Kernel/drivers/devfs/devfs.vp 的
|
||||
# FB_GET_INFO / FbInfo 逐字段一致(packed;4×u32 + 2×u64 = 32 字节)。
|
||||
define FB_GET_INFO = 0x4600
|
||||
define FB_WAIT_VBLANK = 0x4601
|
||||
|
||||
@__attribute__(packed)
|
||||
class FbInfo:
|
||||
@@ -50,11 +51,12 @@ def fb_get_info(fd: int, out: FbInfo*) -> int:
|
||||
string.memset(out, 0, FbInfo.__sizeof__())
|
||||
return fs.ioctl(fd, uint32(FB_GET_INFO), out)
|
||||
|
||||
def fb_map(fd: int, size: uint64) -> uint64:
|
||||
"""把 framebuffer 映射进本进程,返回线性起始地址(0 = 失败),之后可直接写像素。
|
||||
def fb_wait_vblank(fd: int) -> int:
|
||||
"""等一次垂直回扫(合成前调用,减轻 tearing)。用户态无 IO 权限,由内核代做。"""
|
||||
return fs.ioctl(fd, uint32(FB_WAIT_VBLANK), None)
|
||||
|
||||
走通用 mmap(fd = 该设备 fd):内核按设备的 mmap_info 把物理区间按页映射进来。
|
||||
"""
|
||||
def fb_map(fd: int, size: uint64) -> uint64:
|
||||
"""把 framebuffer 映射进本进程,返回线性地址(0 = 失败)。"""
|
||||
return syscall.mmap(0, size, uint64(syscall.PROT_READ | syscall.PROT_WRITE),
|
||||
uint64(syscall.MAP_SHARED), uint64(fd), 0)
|
||||
|
||||
|
||||
Reference in new issue
Block a user