|
|
发表于 2004-10-19 11:23:00
|
显示全部楼层
Re:用DDraw画点线到底有多少优势?
学java和学汇编是两个概念,顶尖高手肯定都会汇编
这是《游戏编程指南》里的一些东西:(我也不会汇编,只好直接照抄了)
5.6 程序提速
上面的程序看起来好像很简单,但运行速度很可能会很慢,即使你直接用汇编重写也不会快多少。原因是读显存非常慢,写显存的速度也比写内存慢。解决这个问题的方法是:
(1) 把除了主页面外的所有页面放在内存中(初始化页面时将ddsd.ddsCaps.dwCaps中的 DDSCAPS_OFFSCREENPLAIN后再或( | )一项DDSCAPS_SYSTEMMEMORY),当然也要将主页面的页面描述改一下:
ddsd.dwSize=sizeof(ddsd);
ddsd.dwFlags=DDSD_CAPS;
ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE;
后台缓冲改为一个普通的离屏页面。这样做的另一个好处是你Lock( )一次后就永远得到了页面指针,而且然后一Unlock( )就又可以使用Blt了。所以你就拥有了两种改变页面的手段。
(2) 将Flip( )和不带透明色的BltFast( )改成直接用memcpy( )拷贝。注意要一行一行地拷贝,比如说640x480x24位色下的全屏幕拷贝是这样的:
BYTE *pSrc=(BYTE *)ddsd_src.lpSurface; //源页面
BYTE *pDest=(BYTE *)ddsd_dest.lpSurface; //目标页面
for (int y=0;y<480;y++)
{
memcpy(pDest, pSrc, 1920); //若为32位色则为2560=640*32/8
pSrc+=ddsd_src.lPitch; //移至下一行
pDest+=ddsd_dest.lPitch; //移至下一行
}
事实上memcpy( )还有进一步“压榨”的余地,下面是用SSE指令实现的超高速memcpy( )。把它拷贝到你的程序中享受完美的速度吧(快80%以上),呵呵(nQWORDs为要拷贝多少个8字节,注意它应能整除8!)。
void Qmemcpy(void *dst, void *src, int nQWORDs)
{
#define CACHEBLOCK 1024 //一个块中有多少QWORDs
//修改此值有可能实现更高的速度
int n=((int)(nQWORDs/CACHEBLOCK))*CACHEBLOCK;
int m=nQWORDs-n;
if (n)
{
_asm //下面先拷贝整数个块
{
mov esi, src
mov edi, dst
mov ecx, n //要拷贝多少个块
lea esi, [esi+ecx*8]
lea edi, [edi+ecx*8]
neg ecx
mainloop:
mov eax, CACHEBLOCK / 16
prefetchloop:
mov ebx, [esi+ecx*8] //预读此循环
mov ebx, [esi+ecx*8+64] //预读下循环
add ecx, 16
dec eax
jnz prefetchloop
sub ecx, CACHEBLOCK
mov eax, CACHEBLOCK / 8
writeloop:
movq mm0, qword ptr [esi+ecx*8 ]
movq mm1, qword ptr [esi+ecx*8+8 ]
movq mm2, qword ptr [esi+ecx*8+16]
movq mm3, qword ptr [esi+ecx*8+24]
movq mm4, qword ptr [esi+ecx*8+32]
movq mm5, qword ptr [esi+ecx*8+40]
movq mm6, qword ptr [esi+ecx*8+48]
movq mm7, qword ptr [esi+ecx*8+56]
movntq qword ptr [edi+ecx*8 ], mm0
movntq qword ptr [edi+ecx*8+8 ], mm1
movntq qword ptr [edi+ecx*8+16], mm2
movntq qword ptr [edi+ecx*8+24], mm3
movntq qword ptr [edi+ecx*8+32], mm4
movntq qword ptr [edi+ecx*8+40], mm5
movntq qword ptr [edi+ecx*8+48], mm6
movntq qword ptr [edi+ecx*8+56], mm7
add ecx, 8
dec eax
jnz writeloop
or ecx, ecx
jnz mainloop
}
}
if (m)
{
_asm
{
mov esi, src
mov edi, dst
mov ecx, m
mov ebx, nQWORDs
lea esi, [esi+ebx*8]
lea edi, [edi+ebx*8]
neg ecx
copyloop:
prefetchnta [esi+ecx*8+512] //预读
movq mm0, qword ptr [esi+ecx*8 ]
movq mm1, qword ptr [esi+ecx*8+8 ]
movq mm2, qword ptr [esi+ecx*8+16]
movq mm3, qword ptr [esi+ecx*8+24]
movq mm4, qword ptr [esi+ecx*8+32]
movq mm5, qword ptr [esi+ecx*8+40]
movq mm6, qword ptr [esi+ecx*8+48]
movq mm7, qword ptr [esi+ecx*8+56]
movntq qword ptr [edi+ecx*8 ], mm0
movntq qword ptr [edi+ecx*8+8 ], mm1
movntq qword ptr [edi+ecx*8+16], mm2
movntq qword ptr [edi+ecx*8+24], mm3
movntq qword ptr [edi+ecx*8+32], mm4
movntq qword ptr [edi+ecx*8+40], mm5
movntq qword ptr [edi+ecx*8+48], mm6
movntq qword ptr [edi+ecx*8+56], mm7
add ecx, 8
jnz copyloop
sfence
emms
}
}
else
{
_asm
{
sfence
emms
}
}
}
同样的,memset也可用SSE指令优化(快300%以上),代码在此(nQWORDs应能被8整除):
void Qmemset(void *dst, int c, unsigned long nQWORDs)
{
__asm
{
movq mm0, c
punpcklbw mm0, mm0
punpcklwd mm0, mm0
punpckldq mm0, mm0
mov edi, dst
mov ecx, nQWORDs
lea edi, [edi + ecx * 8]
neg ecx
movq mm1, mm0
movq mm2, mm0
movq mm3, mm0
movq mm4, mm0
movq mm5, mm0
movq mm6, mm0
movq mm7, mm0
loopwrite:
movntq [edi + ecx * 8 ], mm0
movntq [edi + ecx * 8 + 8 ], mm1
movntq [edi + ecx * 8 + 16], mm2
movntq [edi + ecx * 8 + 24], mm3
movntq [edi + ecx * 8 + 32], mm4
movntq [edi + ecx * 8 + 40], mm5
movntq [edi + ecx * 8 + 48], mm6
movntq [edi + ecx * 8 + 56], mm7
add ecx, 8
jnz loopwrite
emms
}
}
(3) 由于DirectDraw提供的带透明色的BltFast( )此时的工作效率不容乐观,所以我们可以使用RLE压缩掉透明色,同时自己写一个能传送带透明色的图像的函数。你可能已经听过RLE压缩,没听过也没关系,下面就举个例子。如果你定义透明色为(255,0,255),那么这样的一串点:(255,0,255), (255,0,255) , (255,0,255), (255,0,255), (100,23,43), (213,29,85), (255,0,255), (34,56,112), (255,0,255), (255,0,255)可用这样的方式存储:
首先是4个透明点,那么我们在开头放一个0,表示由透明色开头,否则需放一个1。然后是0,12,(表示需跳过4x3=0012个字节),接着是0,2,(即后面为002个图像点),100,23,43,213,29,85,0,3(需跳过003个字节),0,1(后面为001个图像点),34,56,112,0,6(需跳过006个字节)。这样用20个字节就表示出了原来需要30个字节的图像,而且BLT时速度更快,因为无须对每一个点进行判断是否透明色。
一般来说,RLE压缩只在图像的每一行内进行(而不是将整块图像作为一个整体)以方便编程。
如果你还需要更快的速度而不在乎文件大小的话,还有一种比较“邪”的解决方案,其思路是用另外一个程序自动生成成千上万条赋值指令实现BLT。这种办法可以省去大量判断和跳转指令所耗的时间,速度显然达到顶峰。
看起来好像要重写很多东西,其实改动的部分并不多。这样改了之后整个程序的速度就会快很多,但还不能很好地满足全屏幕特效的要求,因为全屏幕特效实在很耗时间,只有用汇编和MMX等指令重写速度才能比较快。所以在下面一章中,我们将介绍内嵌汇编和MMX指令。当然,你直接用5.7节的现成代码也行。
|
|