DX11.2の目玉機能。Tiled Resouce(2)

引き続き、DX11.2の新機能である、Tiled Resouceを見てみたいと思います。


Direct3D Tiled Resources sample (Windows 8.1)
先の記事で、SDKに追加された、新しいリソース形式と、それを取り扱うメソッドについて見てきました。次は、Microsoftのサンプルコード内での実際の使用シナリオに沿って見ていきたいと思います。

タイルリソースにNULLタイルをMappingする

まず、作成したタイルリソース(テクスチャ)の全領域に、NULLタイルをアサインする方法を見てみます。全ての領域にNULLタイルをアサインすることで、このリソースが必要とするメモリが、理論上0になります。
デバイスにリセットがかかった後や、タイルを再構築する際などに必要になると思われる処理です。

ID3D11Texture2D *tiledTexture;
UINT flags = D3D11_TILE_RANGE_NULL;

context->UpdateTileMappings(
            tiledTexture,
            1,
            nullptr, // Use default coordinate of all zeros.
            nullptr, // Use default region of entire resource.
            nullptr,
            1,
            &flags,
            nullptr,
            nullptr,
            0);

UpdateTileMappingを使用します。タイルリソースには、NULLをアサインするリソースを指定します。タイルリソースの範囲を指定する引数には、nullptrを渡します。このような指定の仕方をすることで、リソースの全領域を指定することになるようです。
続けてタイルプールを指定する引数にも、nullptrを指定します。続くタイルプールの範囲指定の引数にも、nullptrを指定します。ただし、rangeFlagには、D3D11_TILE_RANGE_NULLを指定します。
このように指定することで、タイルリソースの全領域にNULLタイルをアサインすることが出来ます。

タイル1枚分のデータをタイルプールに転送する

タイルのデータを転送する方法はいくつかあると思われますが、もっとも基本的と思われる方法が、以下の方法になると思われます。
まず初めに、タイルテクスチャで使用するタイルサイズ1枚分の、一時的なタイルリソースを作成します。
続けて、UpdateTileMappingsで、タイルプール内の転送先の領域を、この一時的なリソースにマップします。
そして、UpdateTilesを用いてタイル一枚分のデータを転送することで、タイルプールにタイル1枚分のデータを転送します。
最後に、不要となった一時的なタイルリソースを破棄します。タイルリソースを破棄しても、メモリの実体である、タイルプールの内容は保持されます。

// 一時的なタイルリソースの作成
D3D11_BUFFER_DESC tempBufferDesc;
ZeroMemory(&tempBufferDesc, sizeof(tempBufferDesc));
tempBufferDesc.ByteWidth = SampleSettings::TileSizeInBytes;
tempBufferDesc.MiscFlags = D3D11_RESOURCE_MISC_TILED;
tempBufferDesc.Usage = D3D11_USAGE_DEFAULT;
ID3D11Buffer *tempBuffer;

device->CreateBuffer(
        &tempBufferDesc,
        nullptr,
        &tempBuffer);

// タイルプールの先頭をマップする
ID3D11Buffer *tilePool;
D3D11_TILED_RESOURCE_COORDINATE startCoordinate;
ZeroMemory(&startCoordinate, sizeof(startCoordinate));
D3D11_TILE_REGION_SIZE regionSize;
ZeroMemory(&regionSize, sizeof(regionSize));
regionSize.NumTiles = 1;
UINT rangeFlags = 0;
UINT tileOfs = 0;
UINT tileNum = 1;

context->UpdateTileMappings(
        tempBuffer,
        1,
        &startCoordinate,
        &regionSize,
        tilePool,
        1,
        &rangeFlags,
        &tileOfs,
        &tileNum,
        0);

// タイルデータを転送する(タイルプールの内容の書き換え)
byte defaultTileData[SampleSettings::TileSizeInBytes];
ZeroMemory(defaultTileData, sizeof(defaultTileData));
context->UpdateTiles(
        tempBuffer,
        &startCoordinate,
        &regionSize,
        defaultTileData,
        0);

// 一時的なタイルリソースの破棄(プールの内容は保持される)
tempBuffer->Release();

1枚のタイルをタイルリソースの全領域にマップする

タイルリソースでは、リソースの複数の領域に、単一のプールの領域をマップすることが出来ます。以下は、タイルプールの先頭1枚分の領域を、タイルリソース全体にマップする例です。
単一のタイルを、複数の領域にアサインする場合は、rangeFlagにD3D11_TILE_RANGE_REUSE_SINGLE_TILEを指定します。また、アサインするタイルの枚数を指定する第9引数にはnullptrを指定できます。

// タイルプールの先頭をすべてのタイルにアサインする
ID3D11Buffer *tilePool;
ID3D11Texture2D *tiledTexture;
UINT nbTotalTiles;

D3D11_TILED_RESOURCE_COORDINATE startCoordinate;
ZeroMemory(&startCoordinate, sizeof(startCoordinate));
D3D11_TILE_REGION_SIZE regionSize;
ZeroMemory(&regionSize, sizeof(regionSize));
regionSize.NumTiles = nbTotalTiels;
UINT rangeFlags = D3D11_TILE_RANGE_REUSE_SINGLE_TILE;
UINT tileOfs = 0;

context->UpdateTileMappings(
        tiledTexture,
        1,
        &startCoordinate,
        &regionSize,
        tilePool,
        1,
        &rangeFlags,
        &tileOfs,
        nullptr,
        0);

実は、この処理は、Tier1デバイスのタイルリソースで、有用な処理のようです。
タイルリソースの使用にあたり、まず初めに、ダミーとなるタイルを、全領域にアサインすることで、シェーダー側で、テクスチャのどの領域にアクセスした場合でも、必ず何らかのタイルがアサインされている状態を作ることが出来ます。
Tier1デバイスにおいて、タイルがアサインされていない領域のサンプリングを行った時の動作は、おそらく未定義なものと思われ、不定な動作を招くと思われます。この処理は、それを防ぐ意味があるようです。

PackedMipにタイルをアサインする

タイルテクスチャを扱う上で、煩雑な処理になりそうなものの一つが、PackedMipの扱いです。
PackedMipは、複数の小さなミップマップが、1枚のタイルにアサインされています。領域自体がそれほど大きくないのは自明なので、常にタイルをアサインしたままにしておくという事も可能だと思われます。
以下では、PackedMipにプールをアサインします。PackedMipの情報は、あらかじめGetResouceTilingメソッドで取得しておきます。また、下記の例では、1枚の2Dテクスチャリソースのケースになります。CubeMapや2DArrayの場合とは異なります。

// PackedMipにタイルをアサインする
ID3D11Buffer *tilePool;
ID3D11Texture2D *tiledTexture;
D3D11_PACKED_MIP_DESC packedMipDesc;

D3D11_TILED_RESOURCE_COORDINATE startCoordinate;
ZeroMemory(&startCoordinate, sizeof(startCoordinate));
startCoordinate.X = packedMipDesc.StartTileIndexInOverallResource;

D3D11_TILE_REGION_SIZE regionSize;
ZeroMemory(&regionSize, sizeof(regionSize));
regionSize.NumTiles = packedMipDesc.NumTilesForPackedMips;

UINT rangeFlags = 0;
UINT reservedTiles;  //プールの未使用領域の先頭

context->UpdateTileMappings(
        tiledTexture,
        1,
        &startCoordinate,
        &regionSize,
        tilePool,
        1,
        &rangeFlags,
        &reservedTiles,
        nullptr,
        0);

reservedTiles += regionSize.NumTiles;

PackedMipのリソース位置を示す、D3D11_TILED_RESOURCE_COORDINATEは、単にXにStartTileIndexInOverallResourceを指定することで、PackedMipのタイルを指定することができます。
アサインするプールのオフセット位置は、他の使用中のタイルと重複しないように指定する必要があります。
どうやら、単純な1対1対応のマッピングの際には、第9引数のTileCountsにはnullptrが指定できるようです。

PackedMip領域にデータを転送する

PackedMipは複数のミップマップが単一のタイルにアサインされているため、UpdateTilesで、データを更新することは困難だと思われます。
GetResouceTilingで取得できる、PackedMipに対応するD3D11_SUBRESOURCE_TILING構造体の、StartTileIndexInOverallResourceメンバにはD3D11_PACKED_TILEが格納されているようです。
つまり、どのPackedMipがどのタイルに格納されているかは未定義のようです。
MicroSoftのサンプルコード内では、先ほどのように、PackedMipにプールをアサインした後に、UpdateSubresouceを用いて通常のテクスチャと同様に転送しています。

// PackedMipのデータをロードする
ID3D11Texture2D *tiledTexture;
D3D11_PACKED_MIP_DESC packedMipDesc;
D3D11TEXTURE2D_DESC textureDesc;

for (int i = 0; i < packedMipDesc.NumPackedMips; i++)  {
  UINT subRes = textureDesc.MipLevels - i - 1;

  UINT pitch; //適切なpitchを設定
  void *data; //適切なMipデータを設定

  context->UpdateSubresource(
          tiledTexture,
          subRes,
          nullptr,
          data,
          pitch,
          0);
}

シェーダー側における取り扱いは、次の記事で見ていきます。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中