« September 2017 | Main | November 2017 »

October 31, 2017

skynet 1.1.0 发布

skynet 1.1 正式发布了。

这个版本的意义主要在于修补了一年前 2016 年 7 月发布 1.0 以来已经发现的 bug 。受益于 skynet 被越来越多的项目使用,很多我们自己使用时未能发现的 bug 通过 github 被定位和修复。

在这个版本中, lua 版本同步到了最新的 5.3.4 并打上了官方发布的 5 个 bugfix ,其中有几个还是我们在使用 skynet 时发现并汇报给 lua 官方的。jemalloc 也更新到了 5.0.1 。有同学报告说在某些测试环境下,jemalloc 的第 5 版性能比前一个版本要差,但我认为随着版本更新,性能有所下降是正常的。只要项目没有大的分叉,使用新的稳定版本都是值得的。

不过 skynet 的这个新版本在 IO 方面应该是比旧版本有性能提高。因为 1.1 版中,网络写操作会尝试先在 IO 线程之外完成。在需要做大量数据发送的场合(例如做流媒体广播),性能会有明显的提升。

1.1 版的 lua 模块放进了专门的名字空间下,这点可能会造成一定的对 1.0 版的兼容性问题。不过长远看,在工程方面是有利的,修改老项目适配 1.1 版的成本也不大。

还有一些小的改进,具体可见 HISTORY.md ,基本都向前兼容。建议还在维护的使用 skynet 1.0 的项目都应尽可能更新到 1.1 版。

October 26, 2017

近期在 bgfx 上做的一些工作

这段时间玩 bgfx ,除了前段公布的 lua binding 外,还颇做了一些别的工作。

首先是帮 bgfx 的 directx 12 driver 打了补丁,使之可以用 mingw build 出来正确运行。也就是之前 blog 上提到的 Direct3D12 的接口设计 bug 。这个 pr 已经被官方接纳。

然后是我给 bgfx 的 debug text 做了中文的支持。 bgfx 专门做了一个层,模拟 VGA 的 text mode 。一开始我学着过去 dos 模式下做中文系统的方式增加了双字节支持,采用在模拟出来的 video memory 的 attribute byte 上设置几个特殊标记来表示接下来的两个 slot 是连起来的一个 unicode 字符。

这种让一个汉子占据两个 video memory slot 的方式在过去非常常见,不过弊端也很明显:容易产生半个汉字乱码问题,解决方案看起来比较 trick 。而且把汉字作为一种特殊字符来处理,而不是彻底解决 unicode 大字符集的解决方案感觉也很不美观。

最开始提交的 pr 果不其然被拒了

接下来我花了一点气力实现一个更加通用的 unicode 方案:

至少要支持多个 code page , 并把原本就支持的 dos 字符集当作 cp437 ,而可以由使用者执行添加新的 codepage ,比如汉字所用的 cp936 。再将不同的 codepage 统一转换为 unicode 。我为多 codepage 编写了一个简单的查找 cache ,字形贴图管理的新模块。cp936 里用到 15 点阵汉字字形是从文泉驿黑体中导出的。另外为了更好的支持 unicode ,把 virtual video memory 里保存 codepoint 的单字节扩展为 3 字节,可以把单个汉字放在一个 slot 里。不过,为了排版正确,还是需要在 debug print 的 api 中检测到汉字就在每个汉字后保留一个空格,让单个汉子占两个英文字符的位置。

这个 patch 现在可以在 bgfx 里使用 ,不过尚未合并到主干。看起来作者也没有拒掉,目前还留在 open 的 pr 列表中。

bgfx 的跨平台 shader 采用了一种奇特的方案。它基本保留了 opengl shader 的语法,仅用 C 风格的预处理语言做了跨平台处理。换句话说,就是用程序加上一堆宏替换和 #ifdef 。这种方法仅仅是利用 cpp 这样的工具就可以了。

不过 bgfx 引入的一个第三方 cpp 工具只能从文件系统处理文本,不能从内存加载文本。所以 shader 的编译流程是一个独立的用 C++ 编写的工具,而没有一个运行时的库。这对于本身基于 C/C++ 开发时不太所谓,反正都有构建流程,无非是让 shader 也进入构建工具链而已。

但如果是用动态语言开发,就麻烦了很多。所以我花了两天时间用 lua 重写了这个 shader 编译工具,采用了一个纯 lua 版本的 cpp 库来做预处理。github 仓库在这里

目前后端编译部分只接了 opengl 尚没有支持 directx ,暂且够用吧。

用 lua 重写的好处是,lua 的文本处理比 C/C++ 强了太多,重写的代码虽然只改了部分,但也清晰了许多,便于维护。现在 shader 也可以直接 runtime 加载了,可以更方便的做 demo 。

bgfx 的作者似乎不太喜欢用动态语言做 runtime 库,但他有计划用 C++ 重构这部分代码,争取把 shader 编译过程做成库可以运行时加载。有这样的预期,我也就没太多兴趣跟进我这个项目了。

btw, 我这里用到的 lua 版 cpp 挺多 bug 的,我边用边改。它似乎是个死项目,提 pr 也没人搭理,只好 copy 过来用,而没有做引用。

October 25, 2017

给 Lua 在 windows 下换上 utf-8 文件名支持

最近在 windows 做开发比较多,lua 原生库使用的都是 C 标准库中的函数,比如文件操作就是用的 fopen 打开文件。这对 unicode 支持的很糟糕。我希望所有和文件名打交道的地方都使用 utf-8 编码,所以今天花了一点时间实现了这么一个库。

我把 lua 原生库中和文件名有关的 api 都重新实现了一遍,包括了:loadfile , dofile , os.rename , os.remove , os.execute, os.getenv , 以及 io.open 。除了 require 都可以在接口上使用 utf-8 字符串了。这里 require 是偷懒没支持 :)

用的比较多的 lfs 库也缺乏 utf-8 支持,我挑选了一部分 api 改写了 utf-8 版本。其中包括 lfs.dir , lfs.currentdir, lfs.chdir, lfs.touch, lfs.mkdir, lfs.rmdir, lfs.attributes 。

对于 windows 还有两个特别的新 api 很可能用得上:

winfile.shortname 可以用来获取短文件名,这样可以很好的去除掉长文件名中的空格。

winfile.personaldir 用来获取 My Document 目录。我可不想在 windows 下使用注册表来保存数据,还是去我的文档下建个目录来存放比较好。

最后: github 仓库传送门

October 21, 2017

BGFX 的一个 lua 封装库

前两年有同学给我推荐了 BGFX 这个库,第一眼被它吸引是它的口号:"Bring Your Own Engine/Framework" style rendering library 。这动不动就说自己是 3d engine 的时代,好好做好一个渲染库,仅仅做好渲染库,是多难得的一件事情。

今年国庆节的时候,偶然间我又翻到这个仓库,居然作者一直在更新。坚持了五年,一直在维护这么个小玩意,让我对这个项目多了点信心。节后我饶有兴趣的研究了一下它的代码。

现在我觉得,这个库的设计思想非常对我的胃口,核心部分几乎没有多余的东西:数据计算、平台 API 支持、数据持久化格式支持、等等都没有放在核心部分。它仅仅只做了一件事:把不同平台的图形 API :Direct X 、OpenGL 等等整合为一套统一的接口,方便在此基础上开发跨平台的 3d 图形程序。不同平台的 3d api 的差异,正是 3d 游戏开发中最脏最累的活了。

虽然 BGFX 已经有人写了 lua binding 库,但我觉得不太合我意:封装的不是很 lua 化,就是简单的 C api 包装,而且 api 覆盖也不全面。

我花了半个月的时间,重新制作了一套 lua 封装。

因为 BGFX 仅仅是渲染库,并不负责创建窗口,获取输入消息。所以我另外整合了 iup 作为窗口框架的支持。因为 iup 已经有很好的原生界面的支持,我在改写 BGFX 自带的 examples 时,就放弃了使用原本例子中用到的 imgui ,转而直接使用 iup 。

在逐个改写 BGFX 自带的例子的过程中,我了解了 bgfx 的架构和 api 设计思路,结合我对 lua 的使用经验,封装为类似但不完全一致的 lua api 。

比如原本 bgfx 的 C/C++ 接口用 VertexDecl::begin VertexDecl::add VertexDecl::end 等一组 api 来构建顶点结构。这是因为 C/C++ 语言本身不太适合描述这类数据。但 lua 有很灵活的数据结构支持,故而只需要一个 api 就可以搞定。

又比如 C/C++ 中用位操作组合预定义的宏来组合整数 flags 是一种常见手法,但是在 lua 中即不那么直观、也未必高效。换成字符串描述就会更好。

C++ 可以重载同名函数、可以把函数调用参数的最后几个设置默认值;而 Lua 则更加灵活,只要类型不同,默认值不一定限制在调用参数的最后几个。

Lua 有一些缺陷:不方便直接操作内存。这回导致直接翻译那些 BGFX 中和内存地址有关,例如更新动态 buffer 这种 API 便不适用了,需要找到合适的方法来封装。

我在做这项封装工作的流程中采用的是挑选有代表性的 example 翻译成 lua 版本。在人工转译的过程中,会发现依赖 BGFX 的 C/C++ API ,就随之实现这些 API 的 lua 封装版本。所以这个封装库是随着转译 examples 逐渐增加而逐步完善的。


我目前的工作环境是 windows 10 64bit ,使用 mingw64 编译器。虽然理论上这是一个跨平台库,我是用的 iup 也是跨平台的。但是目前我尚未有精力去折腾多平台的构建脚本。

有兴趣玩一下,却搞不定编译的同学可以直接下载我编译好的动态库版本。只需要写 lua 脚本就可以试用了。github 仓库在这里。 我在 release 页面上传了我自己编译好的二进制文件。

注意:直接行对行改写的 lua 版本的 example 有时会很低效,尤其是需要做大量数学运算的,例如 02 metaballs 这个例子就比 C 版本慢很多。如果需要兼顾效率,需要重新按 lua 的风格重新组织代码,并把重度计算的地方用 C 写一个库去计算供 lua 调用。

不过大多数 example 运行起来会发现性能和 C/C++ 版本相差无几,完全可以实用。对于做原型,尝试新的图形算法来说,lua 带来的开发便利性无可比拟。