リソース管理( 主にテクスチャ ) の序章!! 〜ブラックトンジ国物語〜 | ゲームプログラマ志望が福岡で叫ぶ 『絶望』

ゲームプログラマ志望が福岡で叫ぶ 『絶望』

プログラマーになりたい!!!!! あ、風のうわさで聞いた最近若者で流行っているトゥイッターなるものを始めてみました (・ト・) @toshi_desu_yo

このブログは DirectX を手玉に取って気持ちよくなるために日々お勉強をする所です。





今回のお客様『( URL )リソース管理のベストプラクティスで御座います。



ワタクシも画像などのリソースはいつもお世話になっております。
使用はしていますが、管理するとなると途端にやる気が無くなるところです

何気なく使っているいつものリソース。
容量が多い画像を複数読み込んだりするとGPUのメモリが無くなり、困ることが出て来ました。




悩める子羊である ksプログラマーはリソースを管理するという壁に対してどうすることも出来ないのでしょうか?

今回のお客様は
泥沼にハマり、ぬるりとしている私を救い上げてくれる解決策をお教えくださるそうなので、是非とも胸を借りる気持ちでわたくしも頑張りたいと思います


(黒柳徹子風)




***************************************
***************************************




僕がハマっている、というかわからないこと。






今やっているプログラムは

読み込む画像が 50 MB 近くあり、
それが数十枚あります。



まず、『なにそれ?』という質問は受け付けません。






それが頻繁に切り替わるので、、

一気に読み込んで使用しています.....が!!





 無  理  
 ( グラボは2GBあるけど何故か落ちる )


(ノД`)シクシク




と、嘆いてる暇はなく。

どうしようかと。。。 



しかし、世の中のゲームは明らかにおかしい。


なぜあんな高品質なテクスチャをいとも簡単に扱えているのか。






知りたいです。


取り敢えずサイズが大きく、複数の画像どう読み込ませるか。

初心者である僕が悩んで考えてみた。 ↓




全部 システムメモリ上でテクスチャを管理する。
使用するシーンのテクスチャはVRAMに。 それ以外は システムメモリ においておく。
 グ   ラ   ボ    増     設




現実的で確実で最も楽なのは ③。


僕もそうしたいのですが、いかんせんクライアント様も予算と都合があるらしいです。

(・д・)チッ




仕方ない。




①はなんとなく却下。


残る②をしたいのですが、どうしたら良いのか???



シーンごとにシステムメモリから VRAM にテクスチャを移行させればいいのだろう!!



けど、やり方がわからん(´ヘ`;)







取り敢えず画像の読み込み時になにをやってるか、

↑のサイトと一緒にお勉強してみる。








↓のようなテクスチャを作成するDirectXのライブラリ。



LoadTexture系ですな。





現在、テクスチャリソースを置く場所( POOL )

D3DPOOL_MANAGED
と書いてありますが、


種類で言えば
D3DPOOL_DEFAULT  や D3DPOOL_SYSTEMMEM 等があります。





こいつら、一体全体何をしているのか??



調べてみたら "テクスチャを置く場所を定義" しているらしいけど。。。





なんとなく説明 ↓



◯ D3DPOOL_DEFAULT
  VRAM に置く

◯   D3DPOOL_SYSTEMMEM
  システムメモリ に置く

◯ D3DPOOL_MANAGED
  VRAMにもシステムメモリにも置かれる






(´・∀・`)ヘー



僕、最初は言われるがままに D3DPOOL_MANAGED を使っていた。



なぜかと老師ワンフーに聞いたら


『ゲーム中にフルスクリーンからウィンドウ画面などに変更した場合、通常は VRAM にテクスチャを作りなおさなきゃいけないけど
  D3DPOOL_MANAGED は同じ物がシステムメモリにとってあるから再度テクスチャを作りなおさなくてもいいんだよ。 』



と、言われました。




そして


『 D3DPOOL_MANAGED は、使用頻度が高いテクスチャは常に VRAM に。
 低いものは VRAM から自動的に削除されて、また使う時に自動的にシステムメモリからVRAM に読みこんでくれるよ。』



とおっしゃってました。




確かに、上を見る限りリソース管理は D3DPOOL_MANAGED が優秀そう。

システムメモリも食べちゃうところはどうなのかわかりませんがメモリに余裕があるならばいいんじゃないかと。



とすると、 D3DPOOL_DEFAULT などは 





------------------------------------------------------------------------------------------

【フルスクリーンからウィンドウに変更 等によるデバイス消失】
  
              ↓
  【テクスチャ開放処理】 → 【再度テクスチャ作成処理】



------------------------------------------------------------------------------------------




という処理を入れなきゃいけないっぽい。


でもその代わりシステムメモリは食べない。。と。






なるほどです!( ´∀`)
 さすが老師!!



実際には学校の先生です。





しかし! わたくしが考えた管理方法は 


『システムメモリに読み込んで、使いたいものだけを VRAM に送りたい。』




D3DPOOL_SYSTEMMEM で読み込んでからなにかで VRAM  に送れるのだろうか???


それとも D3DPOOL_MANAGED で自動的に システムメモリとVRAMを管理してもらう方がいいのか??





さぁ、かなり前置きが長くなりましたが(まじで)

 これも含めて MSDN に 『リソース管理』を教えてもらおうじゃないか!






*************************************
*************************************




GPUがリソースを使うためには、GPUにアクセス出来るメモリに配置する必要がある。




まぁ。 はい。



でも、システムメモリほど容量もない。

そこで GPU が ”システムメモリの一部” にアクセスできる AGP と呼ばれる領域がある。





ここの領域は CPU で読み取り書き込みも行える! 

しかし、フルスクリーンからウィンドウになった場合などのデバイス消失時、
もっかい生成し直さないといけない。




SUGEEEEEEEEE!!!

AGPメモリなんて知らなかったです ←




大部分のリソースは POOL_MANAGED で作成する必要があります。
 → マネージリソースと呼称してみる。





( ゚д゚)! 





● マネージリソース


システムメモリーに作成されてから、必要に応じてビデオメモリにコピーされます。

デバイス消失時にはシステムメモリコピーから自動的に作成されます。


マネージリソースの読み込みでVRAMが足りなくなった場合は使われてないものを自動的に開放する。

この時削除する順序は、時間や優先度を見ます。




優先度は設定可能。

このおかげで、よく使用するマネージリソースは常にVRAMに残すことが出来る。


アプリケーションでは 全てのマネージリソースを強制的に排除するために EvictManagedResources も呼び出すことができます。



次のフレームで必要となるマネージリソースを全て再読み込みする操作には時間がかかる可能性がありますが、

大幅なマネージリソースの移行や、ビデオメモリーの断片化の除去には非常に有効です。






これだ!!!! 

これを使えばシーンごとに変更することが出来るのではないか・・・


ビ・ビデオメモリも断片化があるのか・・・ コレも知らんかった。。

なら全て削除は大いに有効なんじゃないかな。




続き ↓









アプリケーションでのマネージリソースの動きを詳細に見たい場合は
IDirect3DQuery9 インターフェースを使用できる!





デバッグランタイムのみ動くらしい。 リリース時に負担にならないのが良い。





微調整やデバッグに多いに役立つが、あまり詳細に微調整するのは良くない。
 ↓ 

ドライバのバージョンアップやハードウェアが違った場合に動作が変わるから。



あと
今後、DirectX の リソース管理が大幅に向上し、洗礼される( らしい... )から




ほぉ・・・ そんな便利なものがあるとは・・・


使ってみたい( ´∀`)   
というか、使ってみる。









● 既定のリソース



マネージリソースはシンプルで効率的で使いやすいというまさに俺のようなリソース。

しかし、やっぱりビデオメモリを直接使用するのがもっとも良い。




この時は POOL_DEFAULT で作成。


しかし、少しプログラムが複雑になる。

・POOL_DEFAULT リソースはデバイス消失時に対応しなければならない。
・リソースデータを触る場合はパフォーマンスに関する考慮事項を念頭に入れる必要あり。
   → ロックする。等






基本は LOCK しない。 と決めて作成した方が良さそう。





実行時にそのデータへのアクセスが必要で、マネージリソースを使用しない場合は、
システムメモリにあるコピーを使用する。

データを書き換えたら、その結果をロックしたリソースに送るほうが危険性が少ない。







● マネージリソースと既定のリソースの両方の使用



マネージリソースPOOL_DEFAULT リソースを混ぜたものは使えるのか?







ビデオメモリーが断片化。

メモリーが混乱。

それによってアクセス不能。メモリが大幅に無駄。

お前の混合でお前のアプリケーションがヤバイ。

       ______           
     /:υ::─ニjjニ─ヾ          
   /:::li|.:( ○)三 (○)\         
  (:::||!.:υ::::: (__人__)):::: i| ____ 
    ):::::::::::::   |r┬-| li::::/  | |      
  /:::::::::::::::   `ー ' ::::::ヽ  | | 





解決方法


① 全ての POOL_DEFAULT リソースを作成してから POOL_MANAGEDリソースを作成。
② POOL_DEFAULT 作成前に EvictManagedResources 呼び出しをする。
③ 使わない。




混合するなら ① がオススメのこと。






● 動的な既定のリソース




頻繁に生成され、更新されるデータは POOL_DEFAULTで作成し、 USAGE_DYNAMIC を指定するのが最適!



これは、リソースを AGP メモリーに配置するということ。

つまり動的なテクスチャ( サイズを変えたりピクセルの色を変えたり... つまりいじりやすい ) にする



したがって、VRAM にあるときよりGPUのアクセス速度がかなり遅くなる。


しかし、書き換え時などに余分なコピーがない。






POOL_DEFAULTは 普通ロックできない。

UpdateSurface UpdateTexture を使用した時のみ更新することが可能。


こいつらを使用する前に D3DCAPS2_DYNAMICTEXTURES で使用できるかチェックする必要あり。






非常に動的( よく触る )なテクスチャ( ビデオなど )に対しては、POOL_DEFAULT リソースおよび POOL_SYSTEMMEM リソースをアプリケーションで作成し、

 UpdateTexture を使用してビデオメモリーの更新を処理することが最適な選択。



余り書き換えないけど頻繁に VRAM に送る必要が有る場合は 


POOL_MANAGED で作成して、 VRAM に送るほうがコストがヨロシ。







● システムメモリーリソース



リソースは、POOL_SYSTEMMEM でも作成できる。

このリソースは GPUから触れないけど、 UpdateSurfaceUpdateTexture を使用することで、POOL_DEFAULT リソースを更新するためのソースとして使用出来る。


POOL_SYSTEMMEM リソースはシステムメモリーに置かれますが、
GPUドライバーでサポートされる フォーマット( 最大サイズなど ) に準ずる。











ココらへんで一回止める。



わたくしくしが考えていたのは 

POOL_SYSTEMMEM で作成し、
 UpdateSurfaceUpdateTexutre で送ることだったけど、

POOL_MANAGED で作成して自動で管理してもらったほうがパフォーマンス的にはいいのかな・・・



書き換えが頻繁に起きるならば POOL_DEFAULT、POOL_SYSTEMMEM が良いらしい。






でも、膨大なテクスチャの場合はどうなんでしょうかねぇ。。



やっぱり、『システムメモリーに作成して、シーンに合わせてテクスチャをVRAMに送る!』 って場合は POOL_SYSTEMMEM の方がいい気がする。







● 一般的な推薦事項


アプリケーションでリソースを管理する際のベストプラクティス!! ↓






・ 全てのリソースを一番最初に読み込んでおく!
   →  非常に大きなパフォーマンス負荷を与えるが、初期化が終わればスムーズで高速に進む。



・ フレームごとに多数のリソースを作成しない。
   → 一度に複数のリソースを作成すると CPU や GPU に一度に大きな負荷 → ヤバイ
    なので、複数のフレームでリソースの作成を分散するのが良い。



・ フレームの最後に、必ず全てのリソースチャンネル( ストリームソースやテクスチャステージ、現在のインデックス ) の設定を解除。
    → コレにより、実際にもう使用されないリソースが数フレーム以上放置されることが無くなる



・ テクスチャには、ミップマップ付きの圧縮形式を使用したりテクスチャー アトラスの使用も考慮
    → テクスチャアトラス : テクスチャをセットするのを減らすために、
       複数のテクスチャを一枚にまとめて、座標指定で描画をしたり
       1つのテクスチャで描画できるものは全てまとめて描画して、
       テクスチャを入れ替える。 等。 
       つまり、テクスチャを頻繁に入れ替えない。
       


・ リソース管理をあまりこだわらないこと。
   → 特定の組み合わせに対応した細かい調整だと、ドライバー、ハードウェア、OS
    により、互換性の問題が発生する確率が高い。






ふーむ・・・・



なるほど。


僕は目からコイキングが落ちましたよ(´;ω;`)ブワッ




テクスチャの作成だけでも色々と制約があったり長所短所があるんだとわかった。


取り敢えず、、








・ D3DPOOL_DEFAULT で動的テクスチャを作成した場合は AGP と呼ばれる、
『GPUからもCPUからもアクセスできるメモリに配置される。』これにより ムービー などの非常に頻繁にアクセスするテクスチャに対してパフォーマンスが良くなる。




・ 
D3DPOOL_MANAGED と D3DPOOL_DEFAULT の混合はしないこと!
  断片化によるメモリ増大が怖すぎる。




・ D3DPOOK_MANAGED はとてもいいよ!
  なんたって自動管理。デバイス消失時にも自動対応。まさに神。
  しかし、使いたいテクスチャがビデオメモリにない可能性もあるために少し遅くなったりする。
  頻繁に使うものには優先度を設定してVRAMから開放されないようにする!!




・ D3DPOOL_SYSTEMMEM は テクスチャをいじれる。
  テクスチャを膨大に使う場合はこれをVRAMに UpdateTexture で送るのが有効かも。
  そして基本はシステムメモリーに置いておく。




・ 使ったテクスチャステージなどはフレームの最後に自分で外そう!
  コレにより要らないテクスチャが VRAM に残り続けることがない!




・ 膨大なテクスチャの作成は一度のフレームで行うな!
  落ちるよ。いいの?(威圧)




・ そしてあれか、 D3DPOOL_SYSTEMMEM は UpdateTexture 使わないと描画も出来ないのか。もしかして。


・ と、思って色々調べた。


  
  【(URL)D3DPOOL_DEFAULT と D3DPOOL_SYSTEMMEMの二段構え】


  このサイト様のところにあるように、 1つのテクスチャリソースをD3DPOOL_DEFAULT で VRAM。

  そして D3DPOOL_SYSTEMMEM でシステムメモリーにテクスチャをもつ。


  
書き換えた時に TextureUpdate で転送を行う。
  

  というもの。




IDirect3DTexture9::AddDirtyRect と呼ばれる関数も学んだ。
これでUpdateTextureで更新する、「ダーディー領域」と呼ばれる部分を設定するらしい。

【 (URL) ダーディー領域




コレによって『書き換えた部分のみ更新!』って事ができるのではなかろうか。

ダンディーだなぁ!まったく!!!!  



はい。









結論!

 テストしてみないとわからん!!!《゚Д゚》

      



取り敢えず 

 IDirect3DQuery9 でマネージリソースの動きを見てみる。


そして、 D3DPOOL_SYSTEMMEMからVRAM に送る方法以外に、
D3DPOOL_MANAGED で作成したテクスチャを、使った後は EvictManagedResources
で強制的にVRAMから取り除き、新しいシーンでは新しいテクスチャをセットしてみることにする。

そして速度がどうなのか比較したい。





現在は全て D3DPOOL_MANAGED で作成していたので、

まずは IDirect3DQuery9 で動きを見る。