-- MaskBlit 算法的一些探讨及其它测试数据
一个好消息是, 风魂 + 版本正在开发中了. 应很多朋友的要求, 这次接口改用了 C++, 而底层函数却倒退了一步, 几乎全部是汇编, 由于一些特殊的原因, 连函数的堆栈保护 都是自己的代码完成了. 由于构思比较复杂, 所以不打算再支持非 MMX 的指令集.
这次将全面支持 DDraw 的 HAL 层(可选), 但依然继承前作的易用性. 而且增加和 DirectX 的无关性, 比如开发已经持续了 1 周, 但是开发工作依然是工作在 GDI 下 :-) 位图结构有重大改变, 以增加大量的灵活度, 速度虽然会受到一些损失, 但是我将很多函数重写的过程中, 代码得到了进一步的优化, 加上抛弃了非 MMX 版本, 得以放开手脚, 所以两相抵消, 效率还是得到了保证.
令人激动的新特性是, 完全支持了 Alpha Channel (ALpha 通道是位图的属性, 而 Alpha 混合是操作的方法); Z-Buffer (可以自动消隐); Normal-Buffer (实现 3D 光照)
前两天一时兴趣, 将新的代码测试了一下已经完成的代码的速度, 对以后的工作是一个参考, 对想自己写图形底层的朋友也会有一些帮助:
关于测试数据的获取: 我用的 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% 的代码.
简单BLIT | 111373579 | (1.00) |
逐点判断MaskBLIT | 272680257 | (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) |
RLE | 116753109 | (1.05) |
Alpha Channel BLIT | 152241659 | (1.37) |
半透明 BLIT | 239845066 | (2.15) |
Alpha Channel+半透明MaskBLIT | 251453220 | (2.26) |
555模式,半透明MaskBLIT | 275832500 | (2.48) |
半透明MaskBLIT | 288683983 | (2.59) |
半透明MaskBLIT(555模式优化) | 275832500 | (2.48) |
半透明MaskBLIT(Alpha通道优化) | 251453220 | (2.26) |
RLE 压缩,半透明BLITM | 186411836 | (1.67) |
Active混合(565) | 180685290 | (1.62) |
Active混合(555) | 214768297 | (1.93) |
Subtract混合 | 195096412 | (1.75) |
Z-Buffer BLIT | 297367456 | (2.67) |
Alpha通道+ZBuffer BLIT(全遮挡) | 440076735 | (3.95) |
Alpha通道+ZBuffer BLIT(全不遮挡) | 481933812 | (4.32) |