转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=1831

老用户一定记得,在KlayGE 3.9之前,用来表示effect的.fxml都需要通过一个python写成的编译器进行离线编译,生成.kfx格式才能给runtime使用。在3.9里引入了rapidxml,可以直接在runtime读取xml文件,流程变短,开发效率提高了。但随着这两年shader越写越复杂,编译shader所花的时间越来越多,在载入的时候每次都编译shader,一来浪费时间,二来浪费计算。

KlayGE的模型格式.meshml也有过这样的问题,后来通过一个JIT在载入的时候动态把xml的.meshml编译成二进制的.model_bin,之后的载入速度就极大提高了。既不需要离线编译,也不用担心读取效率。因此,引入一个effect JIT也成了理所当然的事情。所以,事隔多年后,kfx格式原地满血复活。

原先的kfx目标是把effect的东西保存成二进制,以便runtime读取;现在的目标是减少时间和计算的浪费。因此,虽然扩展名仍是kfx,要装的东西仍是二进制的effect,但文件格式和能力并没有也不需要和以前的兼容了。新的格式不光有原先的二进制effect部分,更是可以在同一个文件中保存不同API编译后的shader。目前支持的有HLSL bytecode、GLSL、ESSL。不同的渲染插件可以选择自己能处理的格式读取。将来还将支持更多的二进制格式,尤其是OpenGL ES上的,以应对孱弱的OpenGL ES GLSL编译器。

顺便说说4.0上的.fxml_bin格式。因为Android上没有Cg,所以当时应急引入了.fxml_bin,把在PC上JIT好的ESSL存在里面。这样Android上只要直接读取ESSL就可以了,不涉及Cg。但因为只是个应急方案,并没有仔细设计,每个fxml_bin只能保存一个shader。以至于一个fxml可能编译生成许许多多不同的fxml_bin存在目录下。现在终于可以都删掉了。

现在比较一下用了新kfx后的载入速度吧。就选用Deferred Rendering这个shader最多最复杂的例子。

使用kfx之前(秒) 使用kfx之后(秒) 加速(秒)
D3D11 4.8 2.6 2.2
OpenGL 6.0 2.6 3.4
OpenGL ES 28.2 25.3 2.9

D3D11插件省去了组装HLSL、编译HLSL的时间,大约2.2秒。OpenGL的shader需要经历Cg->传统GLSL->现代GLSL的流程,用了kfx之后这些都省掉,所以省下了最多的时间。OpenGL ES和OpenGL流程一样,但ESSL较简单,所以加速没那么明显。需要注意的是OpenGL ES还需要的25秒基本上花在载入纹理上了。因为OpenGL ES模拟器不支持DXT5、SRGB等,需要在载入的时候动态转换成ABGR8,相当慢。以后可能会发展出一个Platform compiler,针对不同平台把资源离线编译出最适合的格式,从根本上解决这个问题。