DIBの大きさと色深度による描画速度

DIBのフレームバッファの容量は、ビットマップの大きさや色深度で容量が大きく変わり描画速度にも差が出てきます。今回は、具体的にいくつかの大きさ・色深度でDIBを作成し、そのDIBをスクロールしながら描画することで実際にどの程度の速度差が出てくるのか、実験してみることにしましょう。

DIBの作成と描画

今回作成するDIBは、色深度が8/24/32bppの3種類です。以下のように指定した大きさで各色形式のDIBを作成するinit()を定義して、大きさを変えながら各色形式のDIBの描画速度を調べてみましょう。

  void init(int w,int h) { /* DIBを指定サイズで初期化 */

      BYTE c;
      LPDWORD lpWork;
      int i;

      iWidth=w;
      iHeight=h;

      if (lpBuf!=NULL)
          GlobalFree(lpBuf);

      /* DIB用バッファ作成 */
      lpBuf=GlobalAlloc(GPTR,sizeof(BITMAPINFO)*3+iWidth*iHeight*8+sizeof(RGBQUAD)*255);

      /* 32bppDIB用ヘッダ設定 */
      lpbi32bpp=(LPBITMAPINFO)lpBuf;
      lp32Pixels=(LPBYTE)(lpbi32bpp+1);
      lpbi32bpp->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
      lpbi32bpp->bmiHeader.biWidth=iWidth;
      lpbi32bpp->bmiHeader.biHeight=iHeight;
      lpbi32bpp->bmiHeader.biPlanes=1;
      lpbi32bpp->bmiHeader.biBitCount=32;
      lpbi32bpp->bmiHeader.biCompression=BI_RGB;

      /* 24bppDIB用ヘッダ設定 */
      lpbi24bpp=(LPBITMAPINFO)(lp32Pixels+iWidth*iHeight*4);
      lp24Pixels=(LPBYTE)(lpbi24bpp+1);
      lpbi24bpp->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
      lpbi24bpp->bmiHeader.biWidth=iWidth;
      lpbi24bpp->bmiHeader.biHeight=iHeight;
      lpbi24bpp->bmiHeader.biPlanes=1;
      lpbi24bpp->bmiHeader.biBitCount=24;
      lpbi24bpp->bmiHeader.biCompression=BI_RGB;

      /* 8bppDIB用ヘッダ設定 */
      lpbi8bpp=(LPBITMAPINFO)(lp24Pixels+iWidth*iHeight*3);
      lp8Pixels=(LPBYTE)lpbi8bpp+sizeof(BITMAPINFO)+sizeof(RGBQUAD)*255;
      lpbi8bpp->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
      lpbi8bpp->bmiHeader.biWidth=iWidth;
      lpbi8bpp->bmiHeader.biHeight=iHeight;
      lpbi8bpp->bmiHeader.biPlanes=1;
      lpbi8bpp->bmiHeader.biBitCount=8;
      lpbi8bpp->bmiHeader.biCompression=BI_RGB;
      lpWork=(LPDWORD)lpbi8bpp->bmiColors;

      for (i=1;i<256;i++) /* カラーテーブル作成 */
          lpWork[i]=(i << 16)+(i << 8)+i;

      /* 各DIBのピクセル列にランダムな色を設定 */
      for (i=0;i<iWidth*iHeight;i++) {

          c=(BYTE)(rand() & 0xff);

          lp32Pixels[i*4]=c;   /* 32bppは青 */
          lp24Pixels[i*3+1]=c; /* 24bppは緑 */
          lp8Pixels[i]=c;      /*  8bppはグレースケール */

      }

  }

初期化関数init()では、まず3枚のDIB用のバッファをまとめて確保します。必要な容量は、BITMAPINFOヘッダ用にsizeof(BITMAPINFO)*3、8ビットDIBのカラーテーブル用にsizeof(RGBQUAD)*255、そしてピクセル列用にiWidth*iHeight*(4+3+1)=iWidth*iHeight*8(4+3+1)は各ピクセル列における1ピクセル当たりのバイト数)なので、

  /* DIB用バッファ作成 */
  lpBuf=GlobalAlloc(GPTR,sizeof(BITMAPINFO)*3+iWidth*iHeight*8+sizeof(RGBQUAD)*255);

として確保します。そして、確保したバッファを以下のようにポインタに分配。

色深度LPBITMAPINFOピクセル列
32lpbi32bpplp32Pixels
24lpbi24bpplp24Pixels
8lpbi8bpplp8Pixels

続いて描画処理。今回は、現在選択されている色深度を変数dwModeに設定してこの変数の値で描画するDIBを変えるようにしました。DIBのピクセル列は、毎回メインループでスクロールさせてから描画します。

  while (1) { /* メインループ */

      if (PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)) {

          if (!GetMessage (&msg,NULL,0,0)) /* メッセージ処理 */
              return msg.wParam ;

          TranslateMessage(&msg);
          DispatchMessage(&msg);

      } else { /* 1ピクセルスクロールして再描画 */

          dwTime2=timeGetTime()-dwTime;
          dwTime=timeGetTime();

          if (dwMode==32) { /* 1ピクセル下スクロール */

              MoveMemory(lp32Pixels,lp32Pixels+iWidth*4,iWidth*(iHeight-1)*4);

              for (i=0;i<iWidth;i++) {

                  c=(BYTE)(rand() & 0xff);

                  lp32Pixels[i*4+iWidth*(iHeight-1)*4]=c;

              }
				
          } else if (dwMode==24) {

              MoveMemory(lp24Pixels,lp24Pixels+iWidth*3,iWidth*(iHeight-1)*3);

              for (i=0;i<iWidth;i++) {

                  c=(BYTE)(rand() & 0xff);

                  lp24Pixels[i*3+iWidth*(iHeight-1)*3+1]=c;

              }

          } else {

              MoveMemory(lp8Pixels,lp8Pixels+iWidth,iWidth*(iHeight-1));

              for (i=0;i<iWidth;i++) {

                  c=(BYTE)(rand() & 0xff);

                  lp8Pixels[i+iWidth*(iHeight-1)]=c;

              }

          }

          InvalidateRect(hwMain,NULL,FALSE);
          UpdateWindow (hwMain);

      }

  }

画面の描画処理では、以下のようにDIBSetDIBitsToDevice()で描画し、ついでにフレームレートも表示してみました。

  void draw(HDC hdc) {

      DWORD dwFps;
      TCHAR lpszStr[64];

      if (dwMode==32) /* 選択されているDIBを描画 */
          SetDIBitsToDevice(hdc,0,32,iWidth,iHeight,0,0,0,iHeight,lp32Pixels,lpbi32bpp,DIB_RGB_COLORS);
      else if (dwMode==24)
          SetDIBitsToDevice(hdc,0,32,iWidth,iHeight,0,0,0,iHeight,lp24Pixels,lpbi24bpp,DIB_RGB_COLORS);
      else if (dwMode==8)
          SetDIBitsToDevice(hdc,0,32,iWidth,iHeight,0,0,0,iHeight,lp8Pixels,lpbi8bpp,DIB_RGB_COLORS);

      if (dwTime2!=0) { /* フレームレート描画 */

          dwFps=(DWORD)(1000/dwTime2);

          wsprintf(lpszStr,"%dFPS",dwFps);

          SetTextColor(hdc,RGB(192,0,255));

          TextOut(hdc,0,0,lpszStr,lstrlen(lpszStr));

      }

  }

ビットマップの大きさや色深度は、以下のようにして作成したコンボボックスで選択します。

  hwBpp=CreateWindow("COMBOBOX",NULL,WS_CHILD|WS_VISIBLE|CBS_DROPDOWN,
    60,4,80,90,hwnd,(HMENU)0,hInst,NULL);

  SendMessage(hwBpp,CB_ADDSTRING,-1,(LPARAM)"32bpp");
  SendMessage(hwBpp,CB_ADDSTRING,-1,(LPARAM)"24bpp");
  SendMessage(hwBpp,CB_ADDSTRING,-1,(LPARAM)" 8bpp");
  SendMessage(hwBpp,CB_SETCURSEL,0,1);

  hwSize=CreateWindow("COMBOBOX",NULL,WS_CHILD|WS_VISIBLE|CBS_DROPDOWN,
    148,4,100,90,hwnd,(HMENU)1,hInst,NULL);

  SendMessage(hwSize,CB_ADDSTRING,-1,(LPARAM)"256*256");
  SendMessage(hwSize,CB_ADDSTRING,-1,(LPARAM)"512*512");
  SendMessage(hwSize,CB_ADDSTRING,-1,(LPARAM)"768*768");
  SendMessage(hwSize,CB_SETCURSEL,1,2);

プログラム

プログラムを実行したら、コンボボックスでDIBの大きさ・色深度を変えて各条件でフレームレートがどうなるか、確かめてみてください。私の環境では、

条件フレームレートセカンドマシン
256*256/8bpp100(333-500)200-250
256*256/24bpp100(250-333)200-250
256*256/32bpp100(250-333)200-250
512*512/8bpp100(125)52
512*512/24bpp100(83)52
512*512/32bpp100(83)66
768*768/8bpp50-100(58)24
768*768/24bpp33-50(38)20
768*768/32bpp33-50(40)30

という感じでした。興味深いことに、BGM奏者でWMAファイルを再生しながら実験する(この時のフレームレートはカッコ内)と、256*256ピクセルの時にフレームレートが跳ね上がる現象が見られました。MCIによるWMA再生時に、マルチメディアタイマ関連の処理が走ってその影響(timeGetTime()の精度向上?)が出ているのかもしれません。また、セカンドマシンでも試してみましたが、いずれも色深度による差はそれほどなく、特にセカンドマシンでは32ビットの方が高速になる傾向が見られました。なお、測定環境は以下のとおりです。

メインマシンCPU - P3-800EB
メモリ - PC133-128MB
ビデオカード - Marvel G400
OS - WindowsXP(1152*864-32bit)
セカンドマシンCPU - P2-400MHz
メモリ - PC100-128MB
ビデオカード - G200
OS - Windows98(1152*864-32bit)

8ビットの方が転送量が少なく高速になるかも、と思ったのですが、環境によってはそうでもないみたいですね。また、ほとんどの環境でこの程度の性能であれば実用上はあまり速度を意識する必要はないのですが、DIBの描画速度は環境によって大きく変わってくるはずです。しかもいろいろな所で話を聞くと、どうもG200/G400の描画速度は例外的に速いのではないか、という気もしてきます。
最近のマシンでは、やや貧弱なメモリ環境にグラフィックを統合したチップセットで構成されるエントリーマシンの存在が気になるところでしょうか。アクションゲームなど、描画処理が重くなりがちなプログラム開発では、テストを十分にした方がよさそうですね。

プログラムダウンロード

プログラミング資料庫>ゲーム制作研究室