开发中的 WindSoul Plus

-- MaskBlit 算法的一些探讨及其它测试数据


一个好消息是, 风魂 + 版本正在开发中了. 应很多朋友的要求, 这次接口改用了 C++, 而底层函数却倒退了一步, 几乎全部是汇编, 由于一些特殊的原因, 连函数的堆栈保护 都是自己的代码完成了. 由于构思比较复杂, 所以不打算再支持非 MMX 的指令集.

这次将全面支持 DDraw 的 HAL 层(可选), 但依然继承前作的易用性. 而且增加和 DirectX 的无关性, 比如开发已经持续了 1 周, 但是开发工作依然是工作在 GDI 下 :-) 位图结构有重大改变, 以增加大量的灵活度, 速度虽然会受到一些损失, 但是我将很多函数重写的过程中, 代码得到了进一步的优化, 加上抛弃了非 MMX 版本, 得以放开手脚, 所以两相抵消, 效率还是得到了保证.

令人激动的新特性是, 完全支持了 Alpha Channel (ALpha 通道是位图的属性, 而 Alpha 混合是操作的方法); Z-Buffer (可以自动消隐); Normal-Buffer (实现 3D 光照)

前两天一时兴趣, 将新的代码测试了一下已经完成的代码的速度, 对以后的工作是一个参考, 对想自己写图形底层的朋友也会有一些帮助:

1458 字节 2430 字节 关于测试数据的获取: 我用的 P200MMX, 内存中测试, 和显存无关, 全部采用软件实现. 用的这样一张图(见右边), 同时有一张 Alpha 通道可选. 这张图的透明部分有 757 个点, 占整个图面积的 29.6% 对于一般游戏中使用, 很有代表性.测试方法是, Blit 这个图 10000 次记录下消耗的时钟周期数. 反复测 10 次, 取最小值. 下面的时间单位都是时钟周期数.

首先, 测试了简单的 Blit 的速度, 其实就是简单的内存复制. 需要是时间是:111373579. 将它定为单位1, 方便下面的比较.

maskblit 操作看似算法简单, 只用每个点比较是不是透明色, 然后复制就可以了. DDraw 里 HEL 层好象就是这样做的(从速度看) 甚至 P6 以上的指令集还增加了 CMOV 指令, 可以提高这种操作的流水线效率. 但是用这个简单的方法并得不到很快的速度. 我的测试是 272680257 (2.45), 我没有 P6 所以没有测 CMOV 带来的好处.

其实 Intel 主页上有一篇MMX 优化算法. 主要是利用 pcmpeqw 指令得到掩码, 然后再处理源和目标点的信息, 再复制, 这样避免了 cmp/jnz 而且一次处理 4 个点 (针对高彩) 速度可以快很多 178006386 (1.60). 不过对于每个像素, 进行了两次读操作, 一次写操作. 如果碰到大量连续的不透明的部分, 就多消耗了一次读内存, 我们可以就此改进.

对于游戏中用到的图片, 往往是大量透明点在一起, 大量不透明的点连在一块, 所以可以在得到掩码后, 先判断是否是连续的两个需要空白点或者两个需要画点, 来选择使用直接跳过/复制,还是利用 MMX. 这样虽然增加了一些判断, 但是对于图片的透明部分和不透明部分 成块的情况下, 减少了一些读操作. 效果比较好. 时间是 173808086 (1.56)

上面这个算法中, 由于只有常规寄存器才能影响标记位, 所以一次只能处理 2 个点. 但是, 如果我们的显卡是 555 模式时, 可以利用高位做 keycolor 标记. 然后利用 mmx 的 pack 指令将 4 个点的透明信息压在 32bit 中供常规寄存器处理. 这样针对 555 优化后的算法, 时间是 166428286 (1.49)

如果还想提高速度, 我一直推崇 RLE 压缩, 当将这张图压缩掉透明部分后, 时间减少到 116753109 (1.05) 由于不需要读写透明部分,它处理的数据量上最少的. 但是代码比较复杂(尤其需要剪裁时, 当然这里测试是不剪裁的情况)

前几天我在优化带 alpha channel 的 blit 时, 偶然发现了另一条新思路。 如果给位图带一张 2 值的 alpha channel. (还是每点 8bit) 利用它做 maskblit 可以带来更快的速度. 我的测试时间是 122750722 (1.10) 很让人激动,不是吗? 这样避免使用 RLE 带来的不能使用硬件加速的问题, 又不失高速度.

下面再来看看其它几个数值:

另外, 风魂 Plus 中将支持 Z-Buffer/N-Buffer, 使用 Z-Buffer, 可以实现非常 Cool 的类 3d 效果. 当然需要 3ds max 之类的软件提供 Sprite 的 Z-Buffer 层. 或者, 可以直接对屏幕分层, 做多层卷轴. 现在 Z-Buffer 部分完成了 50% 的代码.

附表:
简单BLIT111373579(1.00)
逐点判断MaskBLIT272680257(2.45)
MMX版MaskBlit(Intel版)178006386(1.60)
MMX版MaskBlit(云风版)173808086(1.56)
MMX版MaskBlit(555模式优化)166428286(1.49)
MMX版MaskBlit(Alpha通道优化)122750722(1.10)
RLE116753109(1.05)
Alpha Channel BLIT152241659(1.37)
半透明 BLIT239845066(2.15)
Alpha Channel+半透明MaskBLIT251453220(2.26)
555模式,半透明MaskBLIT275832500(2.48)
半透明MaskBLIT288683983(2.59)
半透明MaskBLIT(555模式优化)275832500(2.48)
半透明MaskBLIT(Alpha通道优化)251453220(2.26)
RLE 压缩,半透明BLITM186411836(1.67)
Active混合(565)180685290(1.62)
Active混合(555)214768297(1.93)
Subtract混合195096412(1.75)
Z-Buffer BLIT297367456(2.67)
Alpha通道+ZBuffer BLIT(全遮挡)440076735(3.95)
Alpha通道+ZBuffer BLIT(全不遮挡)481933812(4.32)

返回

云风工作室 制作