使用中 VRAM の計測ができるようになったので、 OpenGL API を使ったときにどのようにメモリが使われていくのかを調べてみました。特に OpenGL の場合にはブラックボックス化された何かという印象が強く、暗黙に消費部分が多いのでは?と感じているためです。この記事エントリ読んでいる方も、そんな風に感じていたりするかもしれません。
環境
プログラム開発PCと調査対象PCを分離して、なるべく影響しないようにして計測しました。また折角なので NVIDIA Geforce, AMD Radeon のそれぞれで調べてみました。Intel HD Graphics はターゲット環境として用意できなかったので未計測です。
使用した OS は Windows 7 64bit で、各ボードでの最新ドライバを使用しました。
- Geforce 9800 340.52
- RADEON HD 5450/7750 Omega 14.12
ボードがちょっと古いですが、傾向はつかめるかなと思います。
免責事項
本調査結果の無断転載を禁止します。本調査はあくまで個人の趣味&好奇心から調べたものであり、結果および考察内容への責任は負いかねます。
実験内容
OpenGL を用いて頂点バッファ(VBO)を作成して、描画。このときに各フェーズでメインメモリと VRAM においてどのように消費されていくかを記録しました。基本的にはOpenGL 2.1世代で標準と思える機能のみを使用して、最近の OpenGL 4.4 系の拡張は未使用です。この状態で、バッファタイプは GL_STATIC_DRAW を設定して、データは VRAM へ配置されることを期待したプログラムを作成しました。
また頂点バッファのサイズは 32MB としました。そして転送元データはOSからの直接アロケートを使用し、プロセスのヒープによる影響を受けないよう工夫しています。
NVIDIA Geforce の場合
NVIDIA の場合には、メモリ使用量の変動幅が大きく、複数回の平均をとっています。
状態 | メモリ(MB) | Dedicate(MB) | 共有メモリ(MB) |
---|---|---|---|
初期化終了後 | 999.8 | 36 | 13 |
元データ準備 | 1031.7 | 36 | 13 |
glBufferData実行後 | 1114.7 | 68 | 32 |
元データ破棄 | 1082.7 | 68 | 32 |
初回描画後 | 1082.9 | 70 | 32 |
上記のような結果になり、データを転送する際には元データ32MBに加えて glBufferData 内部でさらに追加の作業メモリを使用しているようです。それも単に元データのシャドウコピー 32MB という単位ではなく、約 83MB と2倍ちょっと使用しています。
AMD RADEON の場合
AMD の場合にはメモリ変動幅が NVIDIA に比べて低めでした。
状態 | メモリ(MB) | Dedicate(MB) | 共有メモリ(MB) |
---|---|---|---|
初期化終了後 | 1195.7 | 44 | 19 |
元データ準備 | 1228.0 | 44 | 19 |
glBufferData実行後 | 1292.2 | 44 | 19 |
元データ破棄 | 1260.2 | 44 | 19 |
初回描画後 | 1229.9 | 79 | 20 |
上記のような結果になった. glBufferData 内部でのメモリ消費量は 64.2 MB となり、元データの倍ほどの作業メモリがあるようです。初回描画後は転送に使った作業メモリなどが解放されているためか、初期化終了後からバッファ分程度のメモリ増加にとどまっています。(1229.9-1195.7 = 34.2 MB)
また glBufferData 実行では VRAM が消費されていないことがわかりました。実際に処理されるのは初回描画される直前、もしくは、コマンドに積まれてGPUが実行するときまで遅延されるということでしょうか。
まとめ
NVIDIA にしろ AMD にしろ、 glBufferData で作業メモリを消費します。ここまではすでに予測している人も多かったと思います。しかし元データのコピー分だろうと見積もっていたはずです。今回の計測によりそれはハズレであることがわかりました。
少なくとも、元データ以上のサイズが消費され、今回の場合では2倍以上必要になるケースがあることがわかりました。
そして定常状態になった時、 AMD のほうは元データ分だけが消費されているのに対し、NVIDIA は glBufferData 実行直後からおおよそ変動しません。今回のケースでは 32MB の頂点バッファなのに、約 83MB もの領域を消費してしまっています。
メモリがシビアになるケースでは、このことを考慮してプログラムを作成した方がよいかもしれません。