[HMC]年間5250円で独自ドメイン付属、ウィルスチェックありのレンタルサーバ!
Last update : 1999/08/10
DirectDraw基礎 第5回
バックバッファを作成してページフリップを行う
サンプルコードのダウンロード
  では、まずサンプルコードをダウンロードし、解凍して下さい。 私はVisual C++6.0でコンパイルしているので、をお持ちの方は Visual C++でプロジェクトファイルを開いて下さい(「ddraw_05.dsw」をダブルクリックすれば開けます)。 圧縮ファイルに含まれる「ddraw_05.exe」をダブルクリックし、実行してみて下さい。どうでしょう?画面が切り替わり、フルスクリーン化し、「フロントバッファ」と「バックバッファ」が交互に表示されると思います。 今回は、バックバッファ(複合サーフェイス)を作成し、ページフリップを行いたいと思います。
バックバッファ作成
  バックバッファを作成するには、プライマリサーフェイスを複合プライマリサーフェイスとして作成します。複合プライマリサーフェイスとして作成すると 、プライマリサーフェイス以外に、指定した数のサーフェイス(バックバッファ)が自動作成されます。 その後、バックバッファ(サーフェイス)の情報を取得すれば、 バックバッファへ描画する事ができます。

では、まず複合プライマリサーフェイスを作成します。

  第3回でも記述した通り、サーフェイスを作成するにはDDSURFACEDESC構造体のdwFlagに使用するメンバを指定します。 プライマリサーフェイスだけなら、DDSD_CAPSだけでも良いのですが、今回は複合プライマリサーフェイスとしてバックバッファも作成するので DDSD_BACKBUFFERCOUNTも同時に指定します。そうしたらdwBackBufferCountに1を指定します。ここは作成するバックバッファの数を指定するのですが、今回は1つだけ作成します。バックバッファは複数あると、管理しにくくなるという私の独断です(゜゜)☆O(--;)oばこ
  さて、ddsCaps.dwCapsは3つ指定しています。DDSCAPS_PRIMARYSURFACEは、作成するサーフェイスがプライマリサーフェイスである事、DDSCAPS_FLIPは、フリッピングを行えるサーフェイスである事、DDSCAPS_COMPLEXは複合サーフェイスである事を それぞれ表します。というか、ここらは応用のしようが無いので丸暗記で結構です。
  この後、lpDD->CreateSurface(&ddsd,&lpFront,NULL);を実行すると、同時にフロントバッファ、バックバッファを作成し、lpFrontにはフロントバッファ(兼プライマリサーフェイス)が格納されます。 で、これだけではバックバッファへ描画できないので、次にバックバッファを取得します。
GetAttachedSurfaceを使用すると、DDSCAPS構造体のメンバdwCapsへ指定した能力を持つサーフェイスを取得する事が出来ます。 ですから、この関数を使用して、さきほど作成したバックバッファを取得します。ちなみに構文は以下の通りです。

書式HRESULT GetAttachedSurface( LPDDSCAPS lpDDSCaps, LPDIRECTDRAWSURFACE FAR *lplpDDAttachedSurface );
lpDDSCaps取得したいサーフェイス情報を詰め込んだDDSCAPS構造体へのポインタ。
今回はddscapsのアドレスを引き渡しています。
lplpDDAttachedSurface取得したサーフェイスを格納する変数へのポインタ。
今回はlpBackのアドレスを引き渡す。
戻り値 成功した場合はDD_OKが返ってくるらしい。

これで、lpBackにサーフェイスを取得出来ました。また、このサーフェイスは、プライマリサーフェイス等と同じような扱いが出来ます。 ですから、GDIを使用して描画すると言った事も、なんなく出来ます。
フリッピング
では、コードを見てみましょう。
  フリップを行うと、フロントバッファとバックバッファに描かれている内容がそっくりそのまま入れ替わります。 ですから、バックバッファにいろいろ書き込んでおき、フリップを行うと、フロントバッファとバックバッファの内容が入れ替わるので書き込んだ内容が画面に瞬時に表示されます。んで、もう一度フリップすると、 また入れ替わり、元の状態に戻ります。では、Flipの構文を見てみましょう。

書式HRESULT Flip( LPDIRECTDRAWSURFACE3 lpDDSurfaceTargetOverride, DWORD dwFlags );
lpDDSurfaceTargetOverrideサーフェイスを示すポインタです。ここは次の順番のバックバッファ以外のバッファにフリップする時に使用するらしいのですが、普段はNULLを指定して順番通りにフリップさせます。
dwFlagsフリップオプションを指定するフラグです。ここはDDFLIP_WAIT固定と考えて良いでしょう。これは、何らかの原因によって直ぐにフリップ出来ない場合に、フリップ出来るまで待つという指定です。
戻り値 成功した場合はDD_OKが返ってくるらしい。

  このFlipは、複合サーフェイス(フロントバッファ)のメンバとしてのみ呼び出すことが出来ます。ですからlpBack->Flip・・・ と呼び出すことは出来ません。

  フリップは、垂直帰線中(走査線が右下から左上に戻る間)に瞬時に行われるため、ちらつく事がありません。 ちなみに、垂直帰線を待ってからフリップを行うという事なので、ディスプレイの構造上、フリップは秒間60回程度行うのが限界です。
後始末について
  後始末は、今まで通りです。なんとなく「lpBack->Release();」とやりたくなる人もいるかもしれませんが、必要ありません。 というかコレ実行するとフリーズするので良い子はマネしないでね(謎。バックバッファは複合サーフェイスとして フロントバッファと同時に作成されたため、フロントバッファさえ解放してしまえば同時にバックバッファも解放される事になります(たぶん)。
疑似タイマー処理
  さて、今回のサンプルプログラムはどうでしょう?1秒間隔でフリップを実行していると思います。 普段、このようなタイマー処理は、APIを使用して一定時間毎にある関数を呼び出すように設定します。 ですが、これが結構面倒なので、今回は擬似的な処理をしてます。 まず、ビルド → 設定 → リンク → オブジェクト/ライブラリモジュールに「winmm.lib」を書き込む → OK をします。 そして、プログラムの最初のほうで「mmsystem.h」をインクルードします。これでマルチメディア系のAPIが使用できるようになります。 その中にはtimeGetTime();という関数が用意されており、これを呼び出した時点での時間がミリ秒単位で取得出来ます(1000ミリ秒=1秒)。 ですから、メッセージループ周辺を改造して、

とします。こうすると、1番で取得した時間から1000ミリ秒経つまで、ひたすらメッセージループを回す事になります。 ちなみに、こういう一定時間で処理する場合にメッセージループを忘れてしまうと、キー処理がなされなくなり、終了出来ない→電源強制切断という最悪の事態を招くことになります(逝。