March 01, 2024

以非阻塞方式执行一个函数

在 skynet 中,服务之间并行运行,而每个服务自身的业务都是串行的。一个服务由开发者自行切分成多个时间片,每个时间片串行运行在不同的工作线程上。最常见的做法是在每个服务中运行一个 Lua 虚拟机,用 coroutine 切分时间片,这样从编写代码的角度看,任务是连续的。

这个设计的目的是,让开发者轻松享受到多核带来的并发性能优势,同时减轻编写多线程程序带来的心智负担。

用过 skynet 的应该都碰到过:当我们在服务中不小心调用了一个长时间运行而不返回的 C 函数,会独占一个工作线程。同时,这个被阻塞的服务也无法处理新的消息。一旦这种情况发生,看似是无解的。我们通常认为,是设计问题导致了这种情况发生。skynet 的框架在监测到这种情况发生时,会输出 maybe in an endless loop

如果是 Lua 函数产生的死循环,可以通过发送 signal 打断正在运行运行的 Lua 虚拟机,但如果是陷入 C 函数中,只能事后追查 bug 了。

阅读全文 "以非阻塞方式执行一个函数" »

February 22, 2024

关于虚拟文件系统的一些新想法

虚拟文件系统 (vfs) 是 Ant 引擎的核心模块。在 wiki 上有介绍blog 上也有总结

最近在按前段时间拟定的思路重构编辑器。在这个过程中对 vfs 有了一些新想法。短期内不打算把工作重心放到重构 vfs 上面,先记录一下。

最早设计 vfs 的时候,是从网络文件系统的角度看待它的。我把它设想为一个类似 git 的组织方式,带版本控制的网络文件系统。所以,很多设计思路都是延续这个而来。但是,经过了这些年的数次重构,我对最初的思路产生了一些怀疑。

其中,最重要的一条:在游戏运行时,游戏程序看到的 vfs 是一个树结构的不变快照。这样,它像 git 一样,就可以用一个 Merkle tree 的 hash 值就可以代表这个快照,也可以方便的通过网络同步它。

为了实现编辑器,我们在这个设计上打了一些补丁,让编辑器可以在运行时动态的修改它。而我今天反思,“不变快照” 这一点是否是多余的?或者并不需要这个约束,也可以用简单的方案实现现在所有的功能。

阅读全文 "关于虚拟文件系统的一些新想法" »

February 05, 2024

为 log 实现的无锁 Ringbuffer

这两天在改 log 模块。我们需要一个并发写 log 的模块,它有多个 log 生产者一个消费者,这个唯一的消费者在 log 线程中把 log 数据持久化。

大多数 log 生产者是在第三方库的 callback 函数中调用的,比如 bgfx ,如果写 log 不够快的话,就会阻塞渲染。这个 callback 需要自己保证线程安全。因为 bgfx 支持多线程渲染,所以写 log 的 callback 可能在不同的线程触发。

过去在实现 bgfx 的 luabinding 时,我实现了一个简单的 mpsc 队列,get_log 这个函数就是那个单一消费者,它取出队列中所有的 log 信息,返回到 lua 虚拟机中。

它是用 spin_lock 实现的。这两天,我想应该可以实现一个更通用的无锁版本。

阅读全文 "为 log 实现的无锁 Ringbuffer" »

February 04, 2024

一个格式化文本信息版面的小玩意

bgfx 提供了一组调试文本输出的 api ,可以把一些文本信息显示在屏幕上。这些 API 非常简陋,只是提供了一个文本模式缓冲区。离控制台还很远。

具体见 文档中 的 dbgText* 系列函数。

随着我们的游戏引擎中越来越多的信息需要展示,直接使用这些 api 就越发简陋了。最近萌发的想法是干脆使用 imgui 来绘制调试信息界面。但我又觉得保留 bgfx 自带的这个文本模式也有一些好处。

阅读全文 "一个格式化文本信息版面的小玩意" »

January 17, 2024

Ant Engine 开源

我在自己研发的游戏引擎上已经工作了 6 年了。在 2017 年底,我写下了对这个新引擎最初的构想 。现在回头来看,当初的想法居然都落实了,只有一点例外:我们中途把编辑器从 IUP 转移到了 ImGUI 上。

2022 年,我们启动了第一个用这个引擎开发的游戏项目,它是一个和日本公司合作的动作游戏。后来,这个项目没有走下去就取消了。之后,因为我们的引擎开发组喜欢 Factorio ,便想用自己的引擎在手机上重现一个 Factorio Like 的游戏,这一干就是一年多。

现在,游戏的技术部分基本完成,可以验证引擎的可用性(功能完整、性能达标),只是游戏性方面还有不少路要走。简单说就是还不太好玩。

从一开始,我就希望以开源模式经营这个游戏引擎,但同时又觉得没有得到验证的东西不适合拿出来。既然引擎已经初步可用,现在就应该迈开这一步了。

阅读全文 "Ant Engine 开源" »

January 10, 2024

style 表的结构化访问

我们游戏 UI 基于 RmlUI 的 fork,做了大量的改造。它实际上类似目前的 web 前端技术,使用 CSS 来表示 UI 的布局。所以,我们做的底层工作也都是围绕如何高效实现一套基于 CSS 的 UI 引擎来做的。

一年多前,我写过一篇 blog 介绍了一些优化的工作

最近,在游戏开发的使用中,我们又发现了一些性能热点,最近在着手优化。这一篇 blog 记录一下其中的一个优化点。

阅读全文 "style 表的结构化访问" »

December 25, 2023

避免帧间不变像素的重复渲染

上周五在公司内做了一个技术分享,介绍我们最近五年来自研的游戏引擎,以及最近一年用这个引擎开发的游戏。大约有一百多个同学参加了这次分享会,反响挺不错。因为这些年做的东西挺多,想分享的东西太多,很多细节都只是简单一提,没时间展开。

我谈到,我们的引擎主要专注于给移动设备使用,那么优化的重点并不在于提高单帧渲染的速度,而在于在固定帧率下,一个比较长的时间段内,怎样减少计算的总量,从而降低设备的能耗。当时我举了几个例子,其中有我们已经做了的工作,也有一些还没做但在计划中的工作。

我提了一个问题:如果上一帧某个像素被渲染过,而若有办法知道当前帧不需要重复渲染这个像素,那么减少重复渲染就能减少总的能耗。这个方法要成立,必须让检查某个像素是否需要重复渲染的成本比直接渲染它要低一个数量级。之所以现存的商业引擎都不在这个问题上发力,主要是因为它们并没有优先考虑怎么给移动设备省电,而要做到这样的先决条件(可以廉价的找到不需要重新渲染的像素),需要引擎本身的结构去配合。

阅读全文 "避免帧间不变像素的重复渲染" »

December 12, 2023

游戏引擎中的可视化编辑器

提起游戏引擎,特别是商业通用游戏引擎,比如 Unreal 或是 Unity ,给人的第一印象会是它们的可视化编辑器。而在实际开发中,在不同引擎下做游戏开发,影响最大的是引擎层的 API 以及这些 API 带来的模式。

而对于使用自家专有引擎开发出来的游戏,却少见有特别的编辑器。比如以 Mod 丰富见长的 P 社游戏,新系列都使用一个名叫 Clausewitz 的引擎,玩家们在之上创造了大量的 Mod ,却不见有特别的编辑器。Mod 作者多在文本上工作,在游戏本身中调试。游戏程序本身就充当了编辑器:或许只比游戏功能多一个控制台而已。在这类引擎上开发,工作模式还是基于命令行。

阅读全文 "游戏引擎中的可视化编辑器" »

November 30, 2023

游戏数据包的补丁和更新

我们的游戏引擎是基于虚拟文件系统,可以通过网络把开发机上的目录映射到手机上。这对开发非常方便,开发者只需要在自己的开发机上修改资源,立刻就能反应到手机上。

但当游戏发行(也就是我们正在准备的工作),我们还是需要把所有资源打包,并当版本更新时,一次性的下载更新补丁更好。

之前一直没时间做这方面的工作,直到最近才考虑这个问题。我们到底应该设计一个怎样的补丁更新系统。

阅读全文 "游戏数据包的补丁和更新" »

November 24, 2023

Lua 的 C 模块之间如何传递内存块

Lua 的数据类型非常有限,用 C 编写的 Lua 模块也没有统一的生态。在不同模块间传递内存块就是件很头疼的事情。

简单通用的方法就是用 Lua 内建的 string 类型表示内存块。比如 Lua 原生的 IO 库就是这么干的。读取文件接口返回的就是字符串。但这样做有额外的内存复制开销。如果你用 Lua 编写一个处理文件的程序,即使你的处理函数也是 C 编写的模块,也会复制大量的临时字符串。

我们的游戏引擎是基于 Lua 开发的,在文件 IO 上就封装了自己的库,就是为了减少这个不必要的字符串复制开销。比如读一个贴图、模型、材质等文件,最后把它们生成成渲染层用的 handle ,数据并不需要停留在 Lua 虚拟机里。但是,文件 IO 和资源组装(比如贴图构造)的部分是两个不同的 C 模块,这就需要有效的内存交换协议。

阅读全文 "Lua 的 C 模块之间如何传递内存块" »

贴图管理模块及 UI 上的 3D 模型

我们游戏引擎的 UI 使用的是类似网页的技术,是将 RmlUI fork 出来的自行维护的版本 。目前游戏中大量遇到的一个需求是:把 3d 模型作为 UI 组件使用。这个需求在我经历过的历史项目中都曾遇到过,在不同的游戏引擎中我见过各种解决方案。

最典型的是 RPG 类游戏的人物属性面板。通常需要在面板上显示 3D 人物模型。通常还可以旋转这些模型,让玩家 360 度观看。我们目前的游戏类似 Factorio ,没有 Avatar ,但点开建筑的信息面板时,也需要把建筑的 3D 模型动态展现出来。

最初,我们没去细想 3D 渲染怎么和已有的 RmlUI 结合在一起,直接把模型渲染在 UI 层之上。相当于在 UI 模块外开了个后门。UI 上只需要把位置空出来,等 UI 渲染完后,再叠加 3D 模型上去。但这样做的坏处是很明显的:3D 模型无法和 UI 窗口有一致的层次结构。

后来,我们额外构造了一个 render target ,改造了一点 RmlUI ,让它可以支持一个矩形区容纳这个 rendertarget 的画布 。这样,3D 模型渲染就比较好的和 UI 模块融合在一起。但是需要单独编写 UI 上 3d 元素的相关代码,尤其是管理它 ( rendertarget )的生命期。

最近,我希望在 UI 上增加更多 3d 模型。它们仅仅是用来取代原来的 2D 图片。从 UI 角度看,这些就应该是图片,只不过这些图片并不是文件系统中的图片文件,而是运行时由 3d 渲染模块生成的。如果继续沿用目前的图片方案,我们就多出一些开发期处理这些预渲染图片的维护成本。但是,如果直接使用已有方法的话,那个看起来临时的解决方案又有点不堪重负。

阅读全文 "贴图管理模块及 UI 上的 3D 模型" »

November 06, 2023

虚拟文件系统的资源惰性编译

上篇谈了一下我们游戏引擎的虚拟文件系统(vfs)。我觉得这个系统中,游戏资产的管理部分还是个满有意思的设计,值得写一下。

VFS 的设计动机是方便把开发机磁盘上的数据同步到运行设备(通常是手机)中。传统游戏引擎的做法通常是建一个叫做资产仓库的东西,在开发期间不断添加维护这个仓库。需要把游戏部署在运行设备时,再打包上传过去。因为传统游戏引擎在开发期间一般直接在开发机上运行,所以打包上传(从开发机转移游戏资产)并不频繁。

而我们的游戏引擎特别为手机游戏开发而设计,我们不可能直接在手机设备上开发,所以开发机一是和运行机分离的。为了提高开发效率,所以我们设计了 VFS 系统。可以通过网络同步资源仓库。

阅读全文 "虚拟文件系统的资源惰性编译" »

October 25, 2023

游戏引擎的虚拟文件系统

目前我们游戏用的引擎早在 2018 年就开始了。因为一开始,它就定位为一个主要用于手机平台的游戏引擎,要方便手机开发。因为我们不太可能直接在手机设备上编写代码、制作资源,所以开发机一定是和游戏运行环境分离的。从一开始,我们就设计了一个虚拟文件系统,它可以通过网络,把开发机上的文件系统映射到手机设备上,同时兼有版本管理的功能。这样,才可以做到在开发期间,开发机上所做的大多数修改,都能立刻反映到手机上。

我们的游戏引擎的大部分是用 Lua 开发的,这也意味着文件系统中不光有游戏用的资源素材,还包括了代码本身。甚至包括了虚拟文件系统自身的实现。这个东西比一开始想的要麻烦,我们这几年不断地修改它,直到最近。比如一开始认为最麻烦的自举部分 ,在去年就去掉了,为的就是减少系统的复杂度。

阅读全文 "游戏引擎的虚拟文件系统" »

Misc

Categories

Archives

Recent Comments