引き続き、DX11.2の新機能である、Tiled Resouceを見てみたいと思います。
Direct3D Tiled Resources sample (Windows 8.1)
シェーダーコード内でのタイルテクスチャの扱い
結論から言うと、タイルテクスチャは、通常のテクスチャと全く同様にサンプリングすることが可能です。
ただし、その特性上、注意すべき点があります。下記は、タイルテクスチャを利用しているHLSLコードの一例です。
Texture2D ColorTexture : register(t0); Texture2D ColorResidency : register(t1); SamplerState Trilinear : register(s0); SamplerState MaxFilter : register(s1); float diffuseMinLod = ColorResidency.Sample(MaxFilter, tex) * 16.0f; float3 diffuse = ColorTexture.Sample( Trilinear, tex, int2(0), diffuseMinLod);
このコードでは、まず、ColorResidencyテクスチャから、現在タイルがマッピングされているMipMapの状況を読み出します。続けて、タイルテクスチャである、ColorTextureを、読み出したLODの値でClampしつつサンプリングしています。Sampleメソッドの第四引数は、DX11.2で新たに追加されたものようです。(SDKのリファレンスには、SM5以上が必要と書いてあります。)
ColorResidencyテクスチャ
ColorResidencyテクスチャは、タイルテクスチャの転送状況が格納されています。残念ながら、これはユーザー側で作成し、GPUにあらかじめ転送しておく必要があります。
このテクスチャの解像度は、タイルテクスチャであるColorTextureのMipLevel0のタイル数と一致し、各テクセルには、各タイル位置に相当する場所が、最高でどのレベルのMipMapにアクセス可能か(転送されているか)を格納します。従ってこのテクスチャのフォーマットは、8Bitの1Channelで十分です。
この方法を使う前提条件として、テクスチャの同一の場所で、とあるレベルのMipMapまで転送する場合は、その位置の、そのレベル以下の解像度のMipMapは、全てマッピングされているようにします。
つまり、L0が転送されている場合は、その位置の、全てのMipMapがマッピングされているようにします。全体でL8のMipMapで、L5まで転送する場合は、L8~L5までを必ずマッピングします。
また、ColorResidencyテクスチャをサンプリングする際に、MaxFilterというSamplerStateを利用しています。これは、近傍のサンプリングを補間する代わりに、その最大値を返すというSamplerStateです。なぜこれを用いるかというと、タイルの境界付近でサンプリングする際に、近傍のタイルに転送されているMipレベルに差があった場合に、LODの値の高いほう(解像度の低いMip)にあわせることで、マッピングされていない場所のサンプリングを回避します。ちなみにこの機能も、DX11.2で新たに組み込まれたものです。
以上が、シェーダー側でのタイルリソースの扱いです。
Microsoft Build Developer Conf. 2013でのMatt Sandy氏のプレゼン
非常に参考になります。ぜひ一度ごらんになることをお勧めします。
http://channel9.msdn.com/Events/Build/2013/4-063
この一連の記事を書く前に、これを見れれば良かったと思いましたが、残念ながら後の祭りでした。
テクスチャのフォーマットによるタイルサイズの変化
Matt氏のプレゼンを見ると、タイルサイズは、当面の間64KBで固定のようです。つまりGPUのページサイズが64KB(あるいはその約数)だということだと思われます。最近のインテルのCPUは、4KBだったはずなので、それと比べるとずいぶん大きなページサイズだと思います。しかし、実際に扱うリソースの粒度が異なるので、一概に比較することは出来ません。また、GPUは現状page faultをハンドルしないので、このサイズでも問題ないのだと思われます。
とにかく、タイルのサイズが64KBで固定なので、テクスチャのフォーマットが変わると、1タイルあたりに格納テクセル数が変化します。以下がその表になります。
1xMSAA | 4xMSAA | |
---|---|---|
4bpp | 512×256 | 256×128 |
8bpp | 256×256 | 128×128 |
16bpp | 256×128 | 128×64 |
32bpp | 128×128 | 64×64 |
64bpp | 128×64 | 64×32 |
128bpp | 64×64 | 32×32 |
正方形か、横2倍の長方形しかないので、特に難しいことはありません。4xMSAAの場合は、単純にテクセルが数が1/4になるだけです。
MSAAが書いてあるということは、当然ですが、RenderTargetとしてタイルリソースを使用できるということです。Matt氏のプレゼンの中で、タイルリソースのRenderTargetを用いたShadowMapのデモが見られます。興味のある方はご覧になることをお勧めします。
タイルプールのサイズに関する考察
Matt氏のプレゼンの中では、タイルテクスチャを、いわゆるメガテクスチャとして使用した場合、最低限どの程度の大きさのタイルプールを確保するべきかを説明している部分があります。確保すべきタイルプールのサイズは、スクリーンの解像度と、テクスチャのbppに依存し、以下の式で計算できます。
PoolSize = Width x Height * (Total Byte per pixel of Textures) * 4 * 1.333
この式の意味するところは、画面解像度の4倍のテクセルと、そのMipMapを格納する領域ということになります。2倍の解像度が必要な理由は、もし、画面いっぱいにピクセル解像度と同一のテクスチャが表示されている状況で、カメラを少しだけズームした状況では、MipMapを使用したSamplerは、ひとつ上の解像度(画面の2倍解像度)のMipMapにアクセスします。そのため画面の2倍解像度が必要というわけです。加えて、MipMapも格納(1.333倍)できれば、十分な品質を常に保つことが出来るはずであるということです。
ためしに、FullHD(1920×1080)のディスプレイでBC1(4bpp)のテクスチャを使用した場合を計算してみましたが、わずか5.2MBです。
もちろんこれが絶対的な指標というわけではなく、試すべきスタート地点といったところだと思います。実際に必要になるタイルプールのサイズは、ゲーム内における使用用途や、状況などで大きく変化すると思われます。また、3D空間内のオブジェクトに適用された場合は、マッピングしたタイルが、画面上で整列されて表示するわけではないので、PixelByPixelで品質を保証するならば、実際はもっと余裕が必要だと思います。一方で、そこまでの品質が必要ない場合は、タイルプールのサイズはもっと少なくても良いはずです。
まとめ
とりあえずですが、DX11.2の目玉機能であるTiledResouceについて見てみました。
折を見て、今まで書いた3つの記事を、加筆、修正して1つにしてまとめてしまいたいと思います。