タイルリソース
今パートではDX11.2から導入されたタイルリソース(英訳:Tiled Resource)についてみていきます。
タイルリソースはGPU版仮想メモリみたいなもので、GPUのメモリに収まらないほど巨大なデータを扱うときなどに使われます。
もとはメガテクスチャと呼ばれていたりとか、今流行のオープンワールド系のゲームや自動地形生成などで使われているそうです。
が、書いている人自身あまり詳しくないので上の内容が間違っているかもしれません。
ここでは使用法だけについて見ていきます。
タイルリソースはバッファとテクスチャ両方に対応していますので目的にあった方を使用してください。
概要
対応しているプロジェクトはPart17_TiledResourceになります。
タイルリソースはDX11.2に対応しているGPU上でしか利用できません。
そのため一部のGPUではサンプルを実行できない場合がありますがご容赦ください。
タイルリソースの作成
タイルリソースを使う場合に必要となるものはタイルリソースとタイププールの2つになります。
タイルプールがメモリの実態を表し、タイルリソースを介してタイルプールの内容にアクセスします。
まず、タイルリソースの作成について見ていきましょう。
といっても他のリソースとほとんど変わりません。
作成時にMiscFlagsへD3D11_RESOURCE_MISC_TILEDを設定するだけです。
リソースを作成した後は必要に応じてビューを作成することも変わりません。
タイルプールの作成
次にタイルプールの作成について見ていきます。
タイルプールはID3D11Bufferとして作成します。
作成の際はMiscFlagsにD3D11_RESOURCE_MISC_TILE_POOLを指定した上で、サイズが必ず64KBの倍数になるようにしなければなりません。
タイルプールはタイルと呼ばれるものの集合になり、1タイル当たり64KBのサイズとなります。
タイルリソースを扱う上で、タイルの集合体を操作している事を念頭に置くことが大事です。
タイルプールのリサイズ
タイルプールは作成後ID3D11DeviceContext2::ResizeTilePoolでそのサイズを変更することが出来ます。
変更する場合もそのサイズは64KBの倍数か0でなければなりませんの注意してください。
ちなみにID3D11DeviceContext2はID3D11DeviceContext::QueryInterfaceで取得できます。
詳しい使い方は下のコードを参考にしてください。
ドキュメント:ID3D11DeviceContext2::ResizeTilePool
(英語)
タイルリソースが使用するタイル数の調べ方
タイルプールのサイズは自由に変更できますが、実際に必要となるサイズがどの程度か把握できればそれに越したことはありません。
そのような場合はタイルリソースの情報を調べるID3D11Device2::GetResourceTilingを使うと便利でしょう。
ID3D11Device2::GetResourceTilingを使うとタイルリソースに必要となるタイルの数や横と縦と奥行きのタイル数、ミップマップに関係する情報を取得できます。
サンプルでは1つのタイルリソースが必要となるタイル数を調べ、使用するタイルリソース分のタイルを確保しています。
ID3D11Device2もID3D11DeviceContext2と同じくID3D11Device::QueryInterfaceで取得できます。
ドキュメント:ID3D11Device2::GetResourceTiling
(英語)
タイルリソースへタイルプールを紐付け
タイルリソースとタイルプールの両方作成した後はタイルリソースへタイルプールを紐付けます。
紐付け方には直接タイルプールの場所を指定する方法と他のタイルリソースに設定されているものをコピーする方法があります。
直接タイルプールの場所を指定する方法
直接、タイルリソースへタイルプールの場所を指定するにはID3D11DeviceContext2::UpdateTileMappingsを使用します。
ドキュメント:ID3D11DeviceContext2::UpdateTileMappings
(英語)
ID3D11DeviceContext2::UpdateTileMappings
ID3D11DeviceContext2::UpdateTileMappingsの引数は大まかに分けてタイルリソース側とタイルプール側の情報に別れます。
また引数の大半は各々の使用する範囲を指定するものになり、1回の呼び出しで複数の範囲を指定することが可能となっています。
ドキュメントにはより詳細な解説がありますので参考にしてください。
タイルリソース側
- 第1引数:pTiledResource
設定するタイルリソース
- 第2引数:NumTiledResourceRegions
タイルプールのタイルをタイルリソース内のどの範囲に紐付けるかを表すパラメータの個数。
範囲は第3,4引数で指定し、それぞれ開始座標とサイズを指定します。
第3,4引数はここで指定した数の要素を持つ配列を指定する必要があります。
- 第3引数:pTiledResourceRegionStartCoordinates
タイルリソース内の紐付ける場所の開始座標の配列。
D3D11_TILED_RESOURCE_COORDINATEで指定します。
タイルリソースがテクスチャの場合は紐付ける対象となるミップレベルまたは配列の添字も指定する必要があります。
ドキュメント:D3D11_TILED_RESOURCE_COORDINATE:(英語)
- 第4引数:pTiledResourceRegionSizes
タイルリソース内の紐付ける場所の範囲の配列。
D3D11_TILE_REGION_SIZEで指定します。
範囲はバッファなどではタイルの数だけを指定します。
テクスチャの場合はbUseBoxをtrueに指定した上で横、縦、奥行きを指定します。
ドキュメント:D3D11_TILE_REGION_SIZE:(英語)
タイルプール側
- 第5引数:pTilePool
紐付け元となるタイルプールを指定します。
- 第6引数:NumRanges
紐付けを行うタイルプール内の範囲を表すパラメータの個数。
第7,8,9引数には個々で指定した数分の要素を持つ配列を指定する必要があります。
- 第7引数:pRangeFlags
紐付けを行う範囲のフラグを指定します。
D3D11_TILE_RANGE_FLAGで指定します。
ドキュメント:D3D11_TILE_RANGE_FLAG:(英語)
- 何も指定しない(0を指定した時)時:
pRangeTileCountsで指定した数分のタイルを使用します。
- D3D11_TILE_RANGE_REUSE_SINGLE_TILEの時:
1タイルのみ紐付けられます。
その際、pRangeTileCountsには紐付け先となるタイルリソース側のタイルの個数を指定します。
- D3D11_TILE_RANGE_NULLの時:
紐付け先をNULLにします。
- D3D11_TILE_RANGE_SKIPの時:
pRangeTileCountsで指定した数分、紐付け先のタイルリソースのタイルを飛ばして、紐付け先の状態をそのままに保ちます。
これを指定した時はpTilePoolStartOffsetsの値は無視されます。
- 第8引数:pTilePoolStartOffsets
紐付けを行う範囲の開始場所を指定します。
UINTを使いタイル単位で指定します。
- 第9引数:pRangeTileCounts
紐付けを行う範囲のサイズを指定します。
UINTを使いタイル単位で指定します。
pRangeFlagsがD3D11_TILE_RANGE_SKIP以外でかつNumRangesが1の場合はnullptrを指定できます。
その際はタイルリソース側で指定した個数分が紐付けられます。
その他
他のタイルリソースに設定されているものをコピーする方法
他のタイルリソースに設定されているものをコピーするにはID3D11DeviceContext2::CopyTileMappingsを使用します。
ドキュメント:ID3D11DeviceContext2::CopyTileMappings
(英語)
ID3D11DeviceContext2::CopyTileMappings
- 第1引数:pDestTiledResource
コピー先となるタイルリソースを指定します。
- 第2引数:pDestRegionStartCoordinate
コピー先の開始座標を指定します。
- 第3引数:pSourceTiledResource
コピー元となるタイルリソースを指定します。
- 第4引数:pSourceRegionStartCoordinate
コピー元のタイルリソースからコピーする範囲の開始座標を指定します。
- 第5引数:pTileRegionSize
コピー元のタイルリソースからコピーする範囲の範囲を指定します。
- 第6引数:Flags
紐付けを行う際のフラグを指定します。
指定できるのものはD3D11_TILE_MAPPING_FLAGで定義されています。
ドキュメント:D3D11_TILE_MAPPING_FLAG:(英語)
このサンプルではタイルリソースの範囲や開始位置を指定する意味がほとんどありません。
なので、タイルリソースは面倒な設定が必要なものだと感じられるかもしれません。
が、サイズが縦横一万を超えるといった巨大なテクスチャを扱う際には必要不可欠となります。
そのような巨大なテクスチャだと全データがGPUのメモリに収まらないので、現在使用している部分のみをGPUのメモリに読み込ませることになります。
タイルリソースはまさにそういったケースに対応するために追加された機能で、範囲や開始位置を指定しているのは当然のことだと言えます。
内容の更新
タイルリソースにタイププールを紐付けが出来ましたら、今までのようにビューを通してシェーダでその内容へアクセスできます。
またID3D11DeviceContext2::UpdateTilesを使用することで、ID3D11DeviceContext::UpdateSubresourceのようにCPUからデータを転送することも出来ます。
ドキュメント:ID3D11DeviceContext2::UpdateTiles
(英語)
ID3D11DeviceContext2::UpdateTilesの引数は上で使った関数と似ています。
注意点として、転送するデータはタイル単位に生成することを忘れないで下さい。
また、タイルプールの内容を異なるタイルリソース間で共有しているときはID3D11DeviceContext2::TiledResourceBarrierを使用しアクセスの順序を指定する必要があります。
ドキュメント:ID3D11DeviceContext2::TiledResourceBarrier
(英語)
まとめ
このパートではタイルリソースについて見ていきました。
GPUメモリに乗りきらないほど膨大なデータを扱う際に効果を発揮するものになるでしょう。
サンプルコードで正しく使用できているか自信が無いのでMicrosoftが公開しているデモコードを見ることを強く推奨します。
また、サンプルコード作成にはこちらのサイトも参考にしました。そちらも合わせてご覧ください。
補足
タイルリソースに対応しているかの確認の仕方
タイルリソースを使用できるかを確認するにはID3D11Device::CheckFeatureSupportを使用します。
この関数で取得できるものにはいくつかありますが、タイルリソースの場合はD3D11_FEATURE_DATA_D3D11_OPTIONS1を使用します。
ドキュメント:ID3D11Device::CheckFeatureSupport
(日本語)
(英語)