开发者用JIT技术在RP2350微控制器上实现超快位图文字渲染
来源标题:JIT bitmap text rendering for microcontrollers
开发者为其RP2350上的复古电脑项目vic-2350实现了一种超快位图文字渲染技术。该方法核心是JIT,可在运行时为渲染字符串动态生成高度优化的代码,从而支持比例间距和字距调整。该技术已集成到其race-the-beam渲染器中,同样适用于framebuffer写入。
以下正文由 AI 翻译自 Hacker News(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
我一直在开发 vic-2350,这是对 80 年代家用电脑的重新构想,运行在 RP2350 微控制器板上。当然,这样的项目需要文本渲染。虽然位图字体如今已不受青睐,但我选择它们既是为了效率,也是因为在低分辨率下,我更偏爱清晰、经过精心设计的位图字体的美感,而非抗锯齿字体渲染那种模糊的一团。它们仍然有其用武之地,尽管很狭窄。我还想要比例间距和字距调整,同样是因为我喜爱早期 Macintosh 及其前身的美学风格。RP2350 项目中的大多数文本渲染都基于固定字符单元,因为要获得良好的性能并不容易。这篇博文介绍了一种在 RP2350 等微控制器上极速渲染位图文本的技术。我已将其作为我的 race-the-beam 渲染器的一部分实现,但同样的技术也适用于写入帧缓冲区的场景。核心思想是一个 JIT,动态生成高度优化的代码来渲染字符串。
大约在 80 年代中期,计算机显示文本的方式发生了分化。既有的技术是固定单元尺寸,硬件在图像扫描输出到显示器的过程中实时从字体 ROM 中查找字符。其优势是在有限的硬件条件下实现极快的更新和响应速度。许多终端以及家用电脑都是这样工作的。缺点是不够灵活。特别是只支持等宽字体。较新的技术是位图帧缓冲区,最早由源自 Xerox Alto 的一系列计算机开创,经 Apple Lisa 延续,最终随 Macintosh 而流行。帧缓冲区灵活得多,但需要多得多的计算能力来保持更新。在 80 年代早期,你通常会看着文本逐步渲染出来。我还应该提到,家用电脑通常也提供图形模式,但图形模式的分辨率通常低于文本模式,而 1MHz 的 6502(或类似处理器)根本不够强大,所以像文字处理器和电子表格这样的严肃文本程序都使用文本模式。
快进到 RP2350。借助 HSTX 外设,它能够输出 DVI 视频,但填充视频缓冲区仍然完全由软件负责。有些项目选择使用帧缓冲区,但芯片上只有 520kB 的 SRAM,所以帧缓冲区必须在分辨率或位深(或两者)上受到限制,而且最好能让这块 RAM 可供应用程序用于其他目的。我选择的是 race the beam,即逐扫描线填充视频缓冲区。这允许更丰富的视觉效果和低得多的延迟,但对渲染器构成了严苛的性能约束。每像素大约有 6 个周期的预算。我们在预算之内压得越低,就有越多资源可用于渲染其他图形对象以及其他应用功能。
Rust 无法满足性能要求,所以我开始编写 ARM 汇编语言。渲染器以及 vic-2350 的大部分内容现在都用 symbolasm 编写,这是我为极致高性能 Cortex-M33 编程而创造的一种编程语言。采用 JIT 进一步加快了渲染速度。
渲染通常被建模为流水线,vic-2350 渲染器也不例外。应用程序与渲染器之间的接口是显示列表。本质上,它就是字面意思——一个显示对象列表。显示列表是双缓冲的:一个正在被渲染和扫描输出,另一个由应用程序构建。在每个 vsync 时,如果应用已就绪,显示列表就会交换。构建显示列表在计算上并不密集,但应用当然可能正忙于其他事情。由于显示列表在编译时大小未知,它会进行内存分配。显示对象被装入两个向量:一个存放显示对象本身,另一个存放指向第一个向量的索引列表。一个显示对象由 vtable、一个边界矩形以及对象特定的负载组成。分配内存是可以接受的,因为构建显示列表对时序不敏感。渲染则是另一回事。话虽如此,显示列表的内存效率很重要,尤其是因为有两份副本。在呈现之前,索引会按 y0 升序排序。渲染过程中,渲染器维护一个活动列表,它只是与当前扫描线相交的显示对象的子集。在每条扫描线,y1 = y 的对象会从活动列表中删除,y0 = y 的新对象会被插入,这就是之前排序的原因;这正是对象插入活动列表的顺序。活动列表按显示对象顺序维护。然后渲染器只需对活动列表中的每个显示对象调用 render 方法。我很快会做更多优化,跟踪不透明区间并省略对稍后将被覆盖的像素区间的渲染工作,但这不是这里所介绍的文本渲染的根本内容。在对象的开始和结束处(当它被插入和删除时),渲染器会在显示对象上调用 start 和 end 方法。这些方法可以分配暂存空间并做其他工作来为对象的渲染做准备。有一个专用的位图分配器,既快速又确定性。所有这些为我们提供了将文本渲染实现为显示对象实例所需的一切。文本对象本身包含字符串(每个字符 1 字节)、指向字体的指针以及前景色和背景色。RAM 很宝贵,对于双缓冲的显示列表更是如此。在渲染器的早期版本中,我在显示列表中存储了文本的展开版本,当屏幕填满文本时,这造成了不可接受的内存压力。start 方法为 JIT 代码和每像素 1 位的中间缓冲区分配暂存空间,并调用 JIT 编译器,将字符串展开为代码。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。