利用MMX优化64K色Alpha混合算法(转)

利用MMX优化64K色Alpha混合算法(转)[@more@]

  开始使用MMX CPU后,一直在考虑如何用MMX技术加快Alpha混合的操作,尤其是针对目前常用的高彩模式。而早先在国外一个有关游戏编程的邮件列表讨论的结果是MMX不利于对16位色进行Alpha混合操作。让我们先来看看MMX技术相对于普通指令集的更新,来了解一下这个论点的立论。

  

  MMX技术的优势在于,它的寄存器是64位的,而提供了分组模式,可以将寄存器内的数据按8个字节,或4个字,或2个双字同时进行同一操作,方便了大数据量的数据处理。可以成组数据同时作比较操作,这为透明色点的批量判断带来好处。MMX的CPU拥有8个MMX寄存器,在一定程度上缓解了80x86CPU寄存器数量不足的缺陷。

  

  但是它也有诸多不足,比如算术指令不能对四字节字操作。指令结构都不影响标志位。不能对常数立即寻址。MMX系统指令集的指令相当贫乏(连NOT操作也不能直接实现)。当颜色深度是24/32位时,RGB都占8位,这样可以巧妙的利用MMX里的分组乘法指令达到做Alpha混合运算的效果(MMX的乘法相关指令只有对字操作的PMULHW/PMULLW两条,分别是成组数据的乘后取高位和取低位)本文旨在探讨16位色的快速Alpha混合运算,所以此处略去不提。而16位色,红绿蓝各占5或6位,难以被分组分开,所以不利于运用MMX的这些特性。当然另外的解决方法是采用aRGB 4444的结构,其中4位是Alpha通道,每个色素占半个字节,再采用类似的方法。

  

  看过云风去年提出的《16位Alpha混合优化算法》的朋友,应该会联想到这个算法向MMX的引申,好了,也许你已经明白了大概,本文的理论基本点就在此,唯一的问题是,我们需要面对的是MMX指令集的种种缺陷,这些在实际的程序设计中会逐步的体现出来,下面,云风将在介绍算法的同时,附带的提出一些运用MMX的技巧。先来看看上次的算法有无可进一步优化的可能:

  16位下Alpha混合的关键在于如何将RGB分离,让随后的乘法结果不至于相互干扰。我提出的是将16位的“rrrrrggggggbbbbb”扩展到32位变形成“00000gggggg00000rrrrr000000bbbbb”,即将中间的绿色提到高16位,而使色素间隔都有5到6位,而对于5位的颜色,超过5位的Alpha级别是没有意义的,所以只要设定Alpha值在0~31间,同时算这3个色素的乘法是不会因为进位造成干扰的。而这里需要多操作一次移位扩展16位到32位,然后需要一次与操作, 将中间间隔位置0,而且结果需要同样复杂的逆操作从32位还原到16位。

  

  改进的思路是直接将两个点交错分离,即“rrrrrggggggbbbbbRRRRRGGGGGGBBBBB”分离成“rrrrr000000bbbbb00000GGGGGG00000”及“00000gggggg00000RRRRR000000BBBBB”两部分,前一部分右移5位后变成“00000rrrrr000000bbbbb00000GGGGGG”,两个数字就都可以同时运算3个色素,其结果后一组右移5位后可以与前一组合并。这样就省去了好几次移位操作,并且数据可以4字节读入,和四字节写,粗看真的效率很高。但是在传统的80x86上却有两点制约了它的运用:

  

  CPU 的寄存器不够用,这个方法光保存数据就需要4个32位的寄存器,虽然EAX、EBX、ECX、EDX刚够用,但是这就使得Alpha混合函数不能直接写在Blit操作里面。必须单写个子程序调用。(不过也值得写尝试一下,不是吗?如果有朋友写好了,希望能给我拜读一下,我在风魂游戏程序库里留了接口,并在注释里提到了函数的具体写法。)

  

  

  2D游戏中,一般都是利用Alpha混合绘制精灵而不是规则的矩形位图,所以这里面还存在着透明色的判断,如果是双点处理,这一步不易实现。(不过也不是没有好的方法,就是代码的长度就长而复杂了)

  

  而MMX却提供了8个寄存器,同时有分组比较的指令,正好弥补了这两点不足,而且利用寄存器有64位的优势可以同时运算4个点。所以我们暂且只用MMX来实现新的想法。(如果你对这个方法用在传统指令集上有兴趣,希望同时操作2个点进行Alpha混合,并写出实际的代码,请和我联系,我非常希望看到风魂的非MMX Alpha混合版本能够进一步优化)

  用MMX来做这项工作,原理差不多(相当简单不是?),也是读入源点和目标点后分离成4个数据放在4个寄存器中。两对间进行Alpha混合,(这样6个色素)最后就两对数据混合的结果合并。不过从现在开始我们就要面对MMX 8个寄存器不够用的困境了。MMX指令不能和64位立即常数一起使用,所以在进行分裂操作的时候用到的掩码要常驻在寄存器内。

  

  如果寄存器足够多的话,可以连掩码的反值也放一个,可惜现在不能这么浪费。处理透明色问题方面,可以先将点和透明色比较得到一个掩码,我们再将混合后的点,及原来的目标图上的点(这个点应当保留一个备份,哎,又去了一个寄存器)分别与掩码逻辑运算合并得到最终的数据写入目标图。这里,需要大量运用的NOT操作,Intel竟然没有在MMX指令集中提供。我们只好用PANDN(取反再与操作)间接完成。(例:可以先用PCMPEQW mm0,mm0(自己和自己比较当然全相等了)生成常数FFFFFFFFFFFFFFFF,用PANDN mm1,mm0就可以将mm1取反。)这里,不再可以利用MMX的分组乘法,(MMX不能对32位数进行乘法操作)所以我们应该用移位和加减法来实现。这样,如果有几级Alpha值,就应该写几个混合函数。最后建立一个函数指针数组,将每级Alpha混合函数依次放入数组。我们在调用时就可以根据需要的Alpha值来调用相应的函数了。

  

  在风魂0.07里,Alpha混合又一次修改了算法,(0.06使用的上述算法,0.07 则没有)这里要感谢网友T&P的新思路。针对分级数比较少的Alpha混合,比如8级,可以用更简单的方法。大家可以注意到,50%的 Alpha时,R=(r1+r2)/2,也可以近似的等于r1/2+r2/2。那么RGB可以方便的同时运算。只需要在移位后做一次简单的与操作即可(0RRRRRGGGGGGBBBB & 011110111101111 = 0RRRR0GGGGG0BBBB)然后,将两个移位后的数据相加就完成了Alpha=50%的混合。这个方法避免了切分和还原数据,所以速度更快。风魂的早期版本,对50%的Alpha度就做了此种特殊处理。但是,它是有误差的,误差在于移位造成的每色素上1/32或1/64的偏差。

  

  下一步我们可以将50%的Alpha值推广到25%、12.5%甚至更小。现在来看一下完成R1*25%+R2*75%,它等于R2+R1*25%-R2*25% = R2+R1/4+R2/4。这里除4的操作和除2原理是一样的即:(RRRRRGGGGGGBBBBB>>2)&0011100111100111。依次类推,X*37.5%+Y*62.5% = (X+Y)/2+Y/8-X/8等等。我们就只需要利用移位和加减法就可以同时完成N个色素的混合了。

  

  再来看看这个方法的缺陷。首先是误差问题,每一组移位取与都会造成最大为1/32的误差,而多次运算有可能使误差累计,所以alpha级别不能分的太多。而且alpha级别分的太细后,使得运算步骤变的很多,不切分直接运算的优势有可能损失掉。而且更致命的一点是,如果想用MMX加速,那么通常AND运算用的掩码应该放在寄存器中(如果放在内存,而MMX不能立即寻址,间接寻址取内存可能不能命中CACHE速度变慢,大规模的混合运算速度损失太多),MMX的寄存器却只有8个。那么多个掩码会使明显的感觉寄存器不够用,但这不失为一种好的方法。风魂库0.07中新的alpha精灵,这一步的算法更改带来了10%左右的速度提升,而画质的损失却几乎没有体现。

  

  最后对关于带Alpha通道的位图的做一点探讨,这里每一个点将带有不同的Alpha值,我们应该合理的协调位图的结构。将Alpha值和颜色信息放在一起是不合算的。这样不利于高速处理。我们可以将所有点的Alpha值提出来放在一起,对于16位的颜色,合理的Alpha级别应该在16级以下。这样可以每一个字节存放两个Alpha值。用一个寄存器作为指向Alpha值区域的指针,读入对应点的Alpha值,调用相应的混合函数运算。但是,这种位图每个点都有可能是不同的alpha值,如此就不能多点同时运算,云风找到了另外的加速方法,要知详情,且看下文分解。

  

   后记

  本文提出的方法,都被云风实践证明可行,请参阅风魂游戏程序库的源代码。你会发现速度相当的快。测试表明,MMX下带Alpha混合的位图操作,仅仅比普通的检查透明色的位图操作慢20%。比不用MMX逐点做Alpha混合快2.7倍。如果采用RLE压缩掉透明色点,去掉对透明色的特殊处理,速度还会有很大的提高。(达到DirectDraw里内存表面(Surface)间关键色检查的位块传送(blit)操作的速度)这个算法的意义在于,16位色下,软件Alpha混合的速度已经足够快,这使游戏中大量运用光影效果不再有速度上的顾虑。

  

  

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/8225414/viewspace-952245/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/8225414/viewspace-952245/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值