動的頂点バッファのNoOverwriteとDiscardの記事への質問とその回答

ひげねこさんのXNA Frameworkブログの記事をみて疑問に思ったところを質問させていただき、ご回答をいただいたものです。私が恥をかいているだけの内容ですが、自分用の資料としてまとめておきます。他の方の参考になるようなら幸い。
1
ひげねこ@猫 @Higeneko

ひにけに #XNAに「動的頂点バッファのNoOverwriteとDiscard」( http://bit.ly/jDZsN9 )を投稿しました。

2011-05-23 15:11:27
ひげねこ@猫 @Higeneko

久々のひにけに投稿、1~2月は忙しくて、3月は震災のごたごた、4月も色々あって気づけばもう5月も後半なんですよね

2011-05-23 15:34:55
順三朗 @junzabroP

@Higeneko この記事の解説によると、Discardオプションを使用してSetDataでVertexBufferを書き換えると、VRAMの断片化が進むということになりますね。

2011-05-23 15:35:46
ひげねこ@猫 @Higeneko

@junzabroP この場合、同じサイズのリソースの確保/解放が連続で行われるので、断片化の原因にはなりませんよ

2011-05-23 15:45:08
順三朗 @junzabroP

@Higeneko 確保/解放が連続ということですが、確保が先で、解放があとになりますよね。すると、メモリのアドレスが変わるということになる。つまり元のアドレスのバッファは使用されない領域となり、そこは断片化しているのでは?

2011-05-23 15:46:47
ひげねこ@猫 @Higeneko

@junzabroP そして、次のフレームで確保が必要になった時には前に解放されていた部分の空きサイズが同じなので、そこが使用されて断片化もなくなります

2011-05-23 15:49:33
順三朗 @junzabroP

@Higeneko なぜ次のフレームで確保されるのでしょうか? SetDataを呼び出したタイミングではないのでしょうか。SetDataもコマンド化されるだけということなのかな。

2011-05-23 15:50:43
ひげねこ@猫 @Higeneko

@junzabroP 例えば最初に確保した領域をAとします。次にDiscardで自動的に確保される領域をBとします。この段階で確保されているメモリはA,Bとなり、GPUがAのリソースを使い終わるとAが解放されます

2011-05-23 15:53:30
順三朗 @junzabroP

質問し続けるのもこのへんにしておこうか。ある程度は自分でも確かめないといけないな。

2011-05-23 15:53:33
ひげねこ@猫 @Higeneko

@junzabroP 次のフレームでDiscardすると、今度は解放されたAの領域を再利用し、Bの領域が解放され、元の状態に戻ります。

2011-05-23 15:55:37
ひげねこ@猫 @Higeneko

@junzabroP 要するにDiscardを設定するということは、一定のメモリ確保、解放のパターンが連続することになるのでドライバ側で単純なキャッシュ処理をすることでメモリ断片化を防ぐことができるのが利点となります

2011-05-23 15:56:48
順三朗 @junzabroP

@Higeneko なるほど。次のフレームで同じ呼び出しをすることが前提なわけですね。私の実装したシステムではIndexBufferもプリミティブごとに用意するし、テクスチャは不要になればすぐ解放する動作のままです。やはり断片化しそうな気がします。

2011-05-23 15:57:42
順三朗 @junzabroP

とはいえ、Vertex/IndexBufferを事前確保してこの部分で断片化を防いだとしても、やはりOutofmemoryエラーは出てしまうんだよな。おそらくTexture/RenderTarget Textureの確保でも事前確保が必要なのだろう。

2011-05-23 15:59:30
ひげねこ@猫 @Higeneko

@junzabroP それは、結構きついですね。PCだと大丈夫かもしれませんが、単純なメモリ管理機能しかないコンシューマー機だとメモリ断片化に苦しみそうです。

2011-05-23 16:03:37
順三朗 @junzabroP

@Higeneko いやー、お恥ずかしながら、不十分なんです。しかもゲームプログラム側でなく、ビデオカード側のドライバの品質面で。RADEONでは256MBあってもだめだったりする(と言ってもRD9600)。一方、インテルではそういったことは起こりにくい。

2011-05-23 16:05:21
ひげねこ@猫 @Higeneko

私は大体、メモリ確保の粒度とタイミングを決めといて断片化を起こさないようにしています。っていうか、Xbox 360の場合は自分で作ったメモリ管理方法が使えたりするので「このリソースはこの領域から」みたいに管理することもできるんですけどね

2011-05-23 16:06:09
ひげねこ@猫 @Higeneko

@junzabroP Radeon 9600の世代だと、頂点バッファの切り替えとかにもコストが掛かるのでインデックス、頂点共にまとめておかないとパフォーマンスが出なかったりするので、特化させるんだったら自前のメモリ管理しないとキツイかもしれませんね

2011-05-23 16:17:44
順三朗 @junzabroP

@Higeneko VertexBufferの切り替え自体のコストですか……。そこまでは検討していませんでした。というかそんなものにコストがかかっていたのか……。とはいえ、30000超えのIndexBufferなどもありますので、全部をまとめるのは無理ですね。

2011-05-23 16:19:22
ひげねこ@猫 @Higeneko

@junzabroP R300世代で1万ポリゴンは重荷なような気がします……

2011-05-23 16:26:04
順三朗 @junzabroP

@Higeneko 先ほど少しお話ししましたが、GUIの各パーツについて、全ての状態を1つづりのVertex/IndexBufferにまとめ、IndexBufferを毎回書き換える方法で描画しています。なので、中は実際にはスカスカなんです。

2011-05-23 16:27:49
ひげねこ@猫 @Higeneko

@junzabroP それも厳しいです(物言いばっかりでごめんなさい、決して悪気はありません)、それだと頂点キャッシュのヒット率が皆無に等しくなるので、逆にインデックスバッファ固定、頂点バッファを書き換える方が速いと思います

2011-05-23 16:34:58
順三朗 @junzabroP

@Higeneko なるほど……。ちょっとサンプルを用意して、ベンチかけてみるべきところですねこれは。

2011-05-23 16:36:21