DrawPrimitiveUp()を減らす〜やっと本題か?〜

そんなに引っ張るネタじゃなかったんだが…。


前回のソースをちょと一般化して、

void DrawTexture( LPDIRECT3DTEXTURE9 pTexture, float x, float y, float width, float height )
// pTexture: テクスチャのポインタ
// x : 転送先X座標
// y : 転送先Y座標
// width : 転送先幅
// height : 転送先高さ
{
  TLVERTEX vertex[6] = // 頂点の配列
  {
    { x-width/2, y-height/2, 1,1,0xffffffff,0,0 },  // 左上
    { x+width/2, y-height/2, 1,1,0xffffffff,1,0 },  // 右上
    { x+width/2, y+height/2, 1,1,0xffffffff,1,1 },  // 右下

    { x+width/2, y+height/2, 1,1,0xffffffff,1,1 },  // 右下
    { x-width/2, y+height/2, 1,1,0xffffffff,0,1 },  // 左下
    { x-width/2, y-height/2, 1,1,0xffffffff,0,0 }   // 左上
  };

  D3DDevice->SetTexture( 0, pTexture );

  D3DDevice->DrawPrimitiveUp( D3DPT_TRIANGLELIST, 2, vertex, sizeof(TLVERTEX) );  // 頂点をビデオメモリに転送
}

こんな感じに。で、D3DDevice->SetTexture( 0, pTexture )ってのが貼り付けるテクスチャを切り替える処理なんですけれども、同じテクスチャをつかいまわして処理するようなパターンを考えると、描画のたびに切り替えるのも無駄な感じがします。ってなわけで、現在のテクスチャを保持しておいて、貼るテクスチャが変わった時だけ切り替えるようにするとよいでしょう。

  //D3DDevice->SetTexture( 0, pTexture );

  static LPDIRECT3DTEXTURE9 pTmpTexture = 0;
  if( pTmpTexture != pTexture )
  {
    D3DDevice->SetTexture( 0, pTexture );
    pTmpTexture = pTexture;
  }

上のSetTexture()しているところをこんなに代えるだけすね。まあ、テクスチャクラスを作るなりして、静的メンバにでも入れておくのが普通でしょう。因みに、今日初めて知ったんですが、ANSIだかでは、「ポインタ同士の比較の結果は不定である」としてるんだそうですね。ひあー、機種依存コードですねー。

さてさて、ようやく表題となってましたDrawPrimitiveUp()なんですが、こいつもできれば呼び出す回数を減らした方がよいとのことです。どうやって減らそうかな…ってちょっと考えると、そもそも四角の画像を2個のポリゴンに描いてビデオメモリに送ってやることで画像を出してますので、2枚の画像を4個のポリゴンに描いて送ってやれば2枚の画像を一回のDrawPrimiriveUp()で送れそうです。そういうことです。さっきのSetTexture()を減らす要領で、あらかじめバッファに溜めていって、一気に画像を送ってやればよいということになります。ただし、SetTexture()を行うタイミングでDrawPrimitiveUp()を呼んでやる必要はあります。そうしないと、意図とは異なるテクスチャが表示されてしまいますから…。

const int BUFF_SIZE = 64; // 64枚まで登録できる

static TLVERTEX VertexBuffer[BUFF_SIZE*3*2];  // 四角のため頂点が6個(三角*2)
static int Pos = 0;

// バッファを空にする
void Flush()
{
  D3DDevice->DrawPrimitiveUp( D3DPT_TRIANGLELIST, Pos/3, VertexBuffer, sizeof(TLVERTEX) );  // 頂点をビデオメモリに転送
  Pos = 0;
}

// バッファへ頂点を登録する
void SetVertex( TLVERTEX* pVertex )
{
  TLBERTEX* pTmpBuffer = &(VertexBuffer[Pos]);
  memcpy( pTmpBuffer, pVertex, sizeof(TLVERTEX)*6 );

  Pos += 6;

  if( Pos >= BUFF_SIZE )  // バッファを使い切ったらバッファを転送する
  {
    Flush();
  }
}

// テクスチャを設定する
void SetTextureBuffer( LPDIRECT3DTEXTURE9 pTexture )
{
  static LPDIRECT3DTEXTURE9 pTmpTexture = 0;

  if( pTmpTexture != pTexture )  // テクスチャか切り替わった時はバッファフラッシュをする
  {
    Flush();
    D3DDevice->SetTexture( 0, pTexture );
    pTmpTexture = pTexture;
  }
}

// 画像の転送
void DrawTexture( LPDIRECT3DTEXTURE9 pTexture, float x, float y, float width, float height )
// pTexture: テクスチャのポインタ
// x : 転送先X座標
// y : 転送先Y座標
// width : 転送先幅
// height : 転送先高さ
{
  TLVERTEX vertex[6] = // 頂点の配列
  {
    { x-width/2, y-height/2, 1,1,0xffffffff,0,0 },  // 左上
    { x+width/2, y-height/2, 1,1,0xffffffff,1,0 },  // 右上
    { x+width/2, y+height/2, 1,1,0xffffffff,1,1 },  // 右下

    { x+width/2, y+height/2, 1,1,0xffffffff,1,1 },  // 右下
    { x-width/2, y+height/2, 1,1,0xffffffff,0,1 },  // 左下
    { x-width/2, y-height/2, 1,1,0xffffffff,0,0 }   // 左上
  };

  SetTextureBuffer(pTexture);
  SetVertex(vertex);
}

と、こんな感じになります。すんません、SetTexture()とか勝手にモジュール化してしまいました…。これで、めでたく、DrawPrimitiveUp()の回数を減らすことに成功したのでした。なお、ここでは触れてないですが、当然行われるPresent()の直前に必ずFlush()を行っておかないと、バッファにたまりぱなしで表示されない画像が出てきてしまいますことはお忘れなく。

ポリゴン話は終わり!