当你创建了一个具有一个主表面和一个后备缓冲表面的复杂表面,就可以进行页面切换了!
标准动画循环需要以下几步:
1、清除后备缓冲(后备缓冲清0)
2、将场景渲染到后备缓冲(在后备缓冲表面绘制像素)
3、用后备缓冲表面切换掉主表面(调用Filp()函数进行页切换)
4、锁定在某个帧速率(例如30fps)
5、重复第一步
一些难以理解的细节:
首先,后备缓冲切换到了主缓冲,后备缓冲会不会变成主缓冲?主缓冲会不会变成后备缓冲?
如果这样需不需要每隔一帧就在主表面上进行绘制(因为如果首先成立,那么每隔一帧主表面就变成了后备缓冲...)?
事实上,指向VRAM的指针是由硬件切换的,从DirectDraw和编程人员的观点来看,后备缓冲表面总是离屏的,而主表面总是可见的。
所以:你总是在后备缓冲中进行绘制,并且每帧与主表面作切换(利用Flip函数实现切换到另外一个表面而不是切换到同主表面相关联的后备缓冲)!!!
(说明不会每隔一帧就在主表面上绘制,始终是在后备缓冲上绘制)
重点知识:
在交换链中用下一个关联的表面切换主表面的函数IDIRECTDRAWSURFACE7::Filp():
原型:
HRESULT Flip(
LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,
DWORD dwFlags
);
lpDDSurfaceTargetOverride用来覆盖切换链,实现切换到另外一个表面而不是切换到同主表面相关联的后备缓冲。dwFlags参数应注意.........
三缓冲:具有两个后备缓冲的复杂表面,或者包括主表面在内共有三个表面的切换链,称为三缓冲。
DirectDraw三缓冲的好处是:你只需要简单的调用Flip(),硬件以循环的方式切换页面。不过对用户来说,仍然只是在一个后备缓冲中进行渲染
因此,三缓冲对用户来说是透明的。
警告:在切换页之前,主表面或者后备缓冲表面都必须被解锁。所以,在调用Flip之前要确保它们已经被解锁。
双缓冲变成页切换的例子:
int Game_Init(void *parms = NULL, int num_parms = 0)
{
// this is called once after the initial window is created and
// before the main event loop is entered, do all your initialization
// here
// create IDirectDraw interface 7.0 object and test for error
if (FAILED(DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL)))
return(0);
// set cooperation to full screen
if (FAILED(lpdd->SetCooperativeLevel(main_window_handle,
DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX |
DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT)))
return(0);
// set display mode to 640x480x8
if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,0,0)))
return(0);
// clear ddsd and set size
DDRAW_INIT_STRUCT(ddsd);
// enable valid fields
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
// set the backbuffer count field to 1, use 2 for triple buffering
ddsd.dwBackBufferCount = 1;
// request a complex, flippable
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
// create the primary surface
if (FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL)))
return(0);
// now query for attached surface from the primary surface
// this line is needed by the call
ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
// get the attached back buffer surface
if (FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)))
return(0);
// build up the palette data array
for (int color=1; color < 255; color++)
{
// fill with random RGB values
palette[color].peRed = rand()%256;
palette[color].peGreen = rand()%256;
palette[color].peBlue = rand()%256;
// set flags field to PC_NOCOLLAPSE
palette[color].peFlags = PC_NOCOLLAPSE;
} // end for color
// now fill in entry 0 and 255 with black and white
palette[0].peRed = 0;
palette[0].peGreen = 0;
palette[0].peBlue = 0;
palette[0].peFlags = PC_NOCOLLAPSE;
palette[255].peRed = 255;
palette[255].peGreen = 255;
palette[255].peBlue = 255;
palette[255].peFlags = PC_NOCOLLAPSE;
// create the palette object
if (FAILED(lpdd->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256 |
DDPCAPS_INITIALIZE,
palette,&lpddpal, NULL)))
return(0);
// finally attach the palette to the primary surface
if (FAILED(lpddsprimary->SetPalette(lpddpal)))
return(0);
// return success or failure or your own return code here
return(1);
} // end Game_Init
/
int Game_Main(void *parms = NULL, int num_parms = 0)
{
// this is the main loop of the game, do all your processing
// here
// make sure this isn't executed again
if (window_closed)
return(0);
// for now test if user is hitting ESC and send WM_CLOSE
if (KEYDOWN(VK_ESCAPE))
{
PostMessage(main_window_handle,WM_CLOSE,0,0);
window_closed = 1;
} // end if
// lock the back buffer
DDRAW_INIT_STRUCT(ddsd);
lpddsback->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL);
// alias pointer to back buffer surface
UCHAR *back_buffer = (UCHAR *)ddsd.lpSurface;
// now clear the back buffer out
// linear memory?
if (ddsd.lPitch == SCREEN_WIDTH)
memset(back_buffer,0,SCREEN_WIDTH*SCREEN_HEIGHT);
else
{
// non-linear memory
// make copy of video pointer
UCHAR *dest_ptr = back_buffer;
// clear out memory one line at a time
for (int y=0; y<SCREEN_HEIGHT; y++)
{
// clear next line
memset(dest_ptr,0,SCREEN_WIDTH);
// advance pointer to next line
dest_ptr+=ddsd.lPitch;
} // end for y
} // end else
// you would perform game logic...
// draw the next frame into the back buffer, notice that we
// must use the lpitch since it's a surface and may not be linear
// plot 5000 random pixels
for (int index=0; index < 5000; index++)
{
int x = rand()%SCREEN_WIDTH;
int y = rand()%SCREEN_HEIGHT;
UCHAR col = rand()%256;
back_buffer[x+y*ddsd.lPitch] = col;
} // end for index
// unlock the back buffer
if (FAILED(lpddsback->Unlock(NULL)))
return(0);
// perform the flip
while (FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT)));
// wait a sec
Sleep(500);
// return success or failure or your own return code here
return(1);
} // end Game_Main
一点细节:当你创建后备缓冲是,DirectDraw可能在系统内存中创建它,而不是在VRAM中(如果没有剩余的显存可用)。那时,你无需做任何事情,DirectDraw
将会用双缓冲仿真页面切换,而在你调用Flip时候将后备缓冲表面拷贝到主表面。(说明:后备缓冲无论是创建在VRAM还是在系统内存,我们都不需要做额外操作,仅需简单的
调用Flip,DirectDraw会为我们完成一切)。
一般情况下,你希望创建的主表面和后备缓冲都在VRAM中,但VRAM是有限的。主表面总是在VRAM中,后备缓冲可能在系统内存。
下节待解决问题:为什么要使用blitter??????????