雙緩衝和後備緩衝

前面的例子都是,直接在主表面上繪東西。對於動畫,直接在主表面上繪,會產生很嚴重的閃爍。解決的辦法是採用雙緩衝或後備緩衝。

雙緩衝

雙緩衝:在離屏緩衝中繪製圖像,然後將其拷貝到顯示錶面。

 

見下面代碼,先把數據放到double_buffer,最後再拷貝到主表面上。

下面Sleep(300);睡眠了0.3秒這麼長的時間是爲了更爲明顯的看到畫面的變化。

代碼下載

 

複製代碼
int Game_Main() { if (window_closed) return 0; if (KEYDOWN(VK_ESCAPE)) { PostMessage(main_window_handle,WM_CLOSE,0,0); return 0; window_closed = 1; } memset((void *)double_buffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT); for (int index = 0; index < 5000; index++) { int x = rand() % SCREEN_WIDTH; int y = rand() % SCREEN_HEIGHT; UCHAR col = rand() % 256; // 先把顏色在於緩衝區中 double_buffer[x + y * SCREEN_WIDTH] = col; } DDRAW_INIT_STRUCT(ddsd); lpddsprimary->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL); // 主表面顯存指針 UCHAR * primary_buffer = (UCHAR *)ddsd.lpSurface; // 再把緩衝區的內容拷貝到主表面。 if (ddsd.lPitch == SCREEN_WIDTH) { memcpy((void *)primary_buffer, (void *)double_buffer, SCREEN_WIDTH * SCREEN_HEIGHT); } else { UCHAR *dest_ptr = primary_buffer; UCHAR *src_ptr = double_buffer; // 一行一行的拷貝 for (int y=0; y < SCREEN_HEIGHT; y++) { memcpy((void *)dest_ptr, (void *)src_ptr, SCREEN_WIDTH); dest_ptr+= ddsd.lPitch; src_ptr += SCREEN_WIDTH; } } if (FAILED(lpddsprimary->Unlock(NULL))) return 0; Sleep(300); return 1; }
複製代碼

後備緩衝

後備緩衝也是離屏表面的一種。它是一些用在動畫鏈中的表面,它們具有和主表面相同的尺寸和色深。當創建主表面的時候也創建它們。它和主表面進行頁面切換,這比雙緩衝方案下所需做的內存拷貝要快得多。

 

創建一個關聯後備緩衝的主表面步驟(複雜表面): 

  1. 把DDSD_BACKBUFFERCOUNT加到dwFlagss標誌字段,把DDSURFACEDESC2 結構的dwBackBufferCount字段設爲有效。(它可以設置後備緩衝的數目)
  2. 把DDSCAPS_COMPLEX | DDSCAPS_FLIP加到DDSURFACEDESC2 結構的ddsCaps.dwCaps
  3. 創建主表面。
  4. 得到後備緩衝有再個方法。設置DDSCAPS2 ddscaps.dwCaps = DDSCAPS_BACKBUFFER; 或 ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;

然後調用 GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)得到後備緩衝。

見下面代碼:

複製代碼
DDRAW_INIT_STRUCT(ddsd); ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; // 增加DDSD_BACKBUFFERCOUNT 表明dwBackBufferCount有效 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP; // 多了 DDSCAPS_COMPLEX | DDSCAPS_FLIP ddsd.dwBackBufferCount = 1; // 後備緩衝的個數 if (FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL))) return 0; // 請求一個後備緩衝 ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; // 得到後備緩衝 if (FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback))) return 0;
複製代碼

以上代碼。Lpddsprimary指向主表面,lpddsback指向後備緩衝表面。

對後備緩衝表面進行存取,也需要對其加鎖和解鎖。在對後備緩衝表面繪圖後,把怎麼把所之圖顯示到主表面呢。這就要用到頁面切換了(而且這個切換是由硬件完成的)。

 

用主表面調用下面的函數來切換。(注意在頁面切換前主表面或後備緩衝表面都必須被解鎖)

HRESULT Flip(LPDIRECTDRAWSURFACE7 lpDDSurfaceTargetOverride ,DWORD dwFlags);

第一個參數:用來覆蓋切換鏈,實現切換到另外一個表面,不是切換到同主表面想關聯的後備緩衝,一般爲NULL。

第二個參數:控制標誌。如下:

DDFLIP_INTERVAL2

2次垂直逆程後切換

DDFLIP_INTERVAL3

3次垂直逆程後切換

DDFLIP_INTERVAL4

4次垂直逆程後切換

這些標誌 在DDCAPS結構中設置了DDCAPS2_FLIPINTERVAL才能起作用。默認值是1.

這些標誌 表明在兩個切換頁之間等待多少垂直逆程,只有在指定的垂直逆程數目達到了,DDraw才爲切換的每一頁返回DERR_WASSTILLDRAWING

DDFLIP_NOVSYNC:執行物理切換時儘量靠近下一條掃描線。

DDFLIP_WAIT:強迫硬件不 在出現問題時立即返回,而是等待直到頁面切換能夠進行爲止。一般就用這個標誌比較多。

代碼下載

複製代碼
int MainGame() { if (window_closed) return 0; if (KEYDOWN(VK_ESCAPE)) { PostMessage(main_window_handle, WM_CLOSE, 0, 0); window_closed = 1; } DDRAW_INIT_STRUCT(ddsd); lpddsback->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL); UCHAR *back_buffer = (UCHAR *)ddsd.lpSurface; // 清除後備緩衝數據 if (ddsd.lPitch == SCREEN_WIDTH) memset(back_buffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT); else { UCHAR *dest_ptr = back_buffer; for (int y=0; y<SCREEN_HEIGHT; y++) { memset(dest_ptr, 0, SCREEN_WIDTH); dest_ptr+=ddsd.lPitch; } } 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; } if (FAILED(lpddsback->Unlock(NULL))) return 0; // 切換主表面,是主表面調用Flip,另外要注意的是調用Flip之前,要先解鎖。 while (FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT))); Sleep(500); return 1 }
複製代碼

 

轉載請保留下面鏈接

http://www.cnblogs.com/fangyukuan/archive/2011/06/07/2074713.html 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章