DirectXで遊ぼう 上のページ

DirectDrawの醍醐味、ページフリッピング
  1. 概要

    ここは、ページフリッピングの概念と実装方法について解説しています。
    以下の内容について解説します。


  2. 重い重いグラフィック描画

    通常グラフィック描画を行う際、ただ単に絵を画面に転送するだけというのはあまり賢い手とは言えません。なぜなら、その描画している様子が、それも重い環境では丸見えになる事があるのです。また、人によっては重いように錯覚する事もあります。
    これを改善するためには、次のような方法を用います。まず描画画面と同じ領域を用意して、まずはそこに必要な情報を書き込みます。そして、その領域から画面へと更新内容を転送する。これをバッファリングと呼びます。
    [表示パーツ]
    [表示パーツ]┼→[画面(640x480)]
    [表示パーツ]

        通常の描画方式の例


    [表示パーツ]
    [表示パーツ]┼→[更新バッファ(640x480)][画面(640x480)]
    [表示パーツ]

        バッファリングの例
    バッファリングは古くはDOSアプリでも使われていたテクニックでして、この方法を使うと重い描画処理でもその描画の様子を隠す事で「それなりに高速に見える」ようにできます。
    この更新バッファをDIBSectionに持たせて画面転送にBitBltを用いる事でバッファリング実現するという方法を私はよく使ってます。DIBSectionは直接画像にアクセスでき、更に現在のスクリーンモードに依存しません。自作レンダリングするときは、これを使わない手は無いですよ。

  3. ページフリッピングの概念

    以下が、ページフリッピングの概要図です。
    [表示パーツ]
    [表示パーツ]┼→[画面B(640x480):非表示]   [画面A(640x480):表示中]
    [表示パーツ]

        ページフリッピング・1
    何となくハズレ臭い図ですけど、勘弁したってください。まぁ、概念さえ分かればいいんですから(笑)
    画面の更新は、非表示画面(画面B)に対して行います。このとき、表示中画面(画面A)はノータッチの状態です。
    非表示画面の更新が終わったら、以下のようにAとBを入れ替えます。
    これがページフリッピングです。
    [表示パーツ]
    [表示パーツ]┼→[画面(640x480):非表示]   [画面(640x480):表示中]
    [表示パーツ]

        ページフリッピング・1
    あとは、画面Aに対して画面を再度更新してまたAとBを入れかえる。それから画面Bに対して……と続いていきます。
    これはバッファリングをハードウェアレベルで行うようにしたものとも解釈できます。フリッピングそのものはハードウェアで切りかえるので、DIBSectionで作った画像をBitBltするよりもずっと速いのですが。
    ページフリッピングはこのような方法で動作するのです。

  4. プライマリサーフェイスの生成

    DirectDrawではプライマリサーフェイスを以下のように作成する事で、フリッピング用のサーフェイスを作る事ができます。
    あとこれは重要な事なのですが、ページフリッピングはフルスクリーンでしか実現する事はできません
    LPDIRECTDRAW ddraw;             // DirectDrawのポインタ(取得済みである事)
    LPDIRECTDRAWSURFACE dds[2];     // サーフェイスの格納先
    DDSURFACEDESC desc;
    DDSCAPS caps;
    HRESULT result;
    
    ZeroMemory(&desc,sizeof(desc));
    desc.dwSize = sizeof(desc);
    desc.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT;
    desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE|DDSCAPS_COMPLEX|DDSCAPS_FLIP;
    desc.dwBackBufferCount = 1;
    if((result = ddraw->CreateSurface(&desc,&dds[0],NULL)) != DD_OK){
        // CreateSurfaceエラー
    }
    
    ZeroMemory(&caps,sizeof(caps));
    caps.dwCaps = DDSCAPS_BACKBUFFER;
    if((result = dds[0]->GetAttachedSurface(&caps,&dds[1])) != DD_OK){
        // GetAttachedSurfaceエラー
    }
    
    dwFlags,ddsCaps.dwCaps,dwBackBufferCountの3つが、通常のプライマリーサーフェイスとは違っています。
    前者2つのフラグ設定はお約束なので、このようにしてください。dwBackBufferCountは、今回は非表示サーフェイスを1つもたせる事として1を設定しておきます。これで表示するサーフェイスはdds[0]に格納できます。
    次に非表示サーフェイスですが、これはGetAttachedSurfaceで取得できます。
    これでdds[0]には表示画面、dds[1]には非表示画面のサーフェイスのポインタが取得できました。

  5. 描画&ページフリッピング

    描画そのものは問題ありません。
    上で取得した、dds[1]に対してBlt等を使ってやる事でOKです。
    これらについては、IDirectDrawSurface その3(描画テクニック)辺りを参考にしてください。
    次にフリッピングの方法です。以下、サンプルプログラム。
    HRESULT result = m_dds[0]->Flip(NULL,0);
    if(result != DD_OK){
        // Flipエラー
    }
    
    これで完了です。これだけの処理で、dds[0]とdds[1]の内容は反転します。
    次に更新する非表示サーフェイスは引き続きdds[1]を使えば良いので、描画ルーチンを余計に作る必要はありません。
    こんな感じに、ページフリッピングは実現が可能です。




上のページ