「 OpenGL 」一覧

BC7について段階的にデコードしてみた


BC7のCPUデコーダーを作っている過程でおもしろいものが確認できたので記事にしてみました。当たり前の話ではあるのですが、視覚化されたケースって無いようなので。
BC7 はいわゆる第2世代のテクスチャ圧縮技術で、各ブロックごとに最適なモードを選択してデータを圧縮しています。このブロックがどんな風に割り当てられているかを、ブロックごとの色分けで塗ってみたら以下のような結果を得られました。

image_partition

この段階でも各ブロックの特徴によってモード選択されているのが確認できます。そのためおおよその画像の検討が付く程度にはなっています。これらのブロック種別を順番に展開してみます。 続きを読む


ASTCの圧縮ノイズを調べてみた


今回は期待のASTC圧縮のノイズを調べてみました。

ASTCについて

詳しいことは各所のサイトで触れられていますのでここでは簡単に。 Adaptive Scalable Texture Compression の略で、ARM が開発しました。DXTCがS3の特許関係で色々とあったためか、 ASTC はロイヤリティフリーなことも取り上げられています。
基本的には他の現世代のテクスチャ圧縮と同じくブロックごとのモードを多数持っていることが特徴です。ASTC 固有の特徴は、なんといってもブロックサイズの変更ができるということではないかと思います。

実験

ASTCではブロックサイズを変更することができ、許容できる範囲で BPP を落として削減することができます。試行錯誤の余地があるといえます。
ここでは BC7 や PVRTC への対抗と考えて、それと同じレベルの BPP でチェックをしてみたいと思います。

圧縮のためのツールとしては Mali Texture Compression Tool を用いています。圧縮品質としては最大の品質レベルを選択してデータを作成しました。

ASTC 8BPP(4×4)での結果

8BPPとなるように 4×4 ブロックで圧縮を行いました。この場合は以下のような結果となりました。次の規格レベルだけあって予想通り綺麗に圧縮できていると思われます。左右に並べたくらいではちょっとわかりません。

compare_std_astc_sample01

compare_std_astc_sample02

ツールのほうでは圧縮前後でノイズ具合も表示できるのでこちらの画像も掲載しておきます。
ノイズは割と控えめな感じかと思われます。 [続きを読む]


ETC2の圧縮ノイズを調べてみた


今回は OpenGL ES 3.0 以降で使えるようになった ETC2 について試してみました。アルファチャンネル入りでの圧縮形式として標準的に使えるようになった形式です。基本的な実装方針は ETC1 の拡張したイメージになっています。

データの作成は Mali Texture Compression Tool を用いて行っています。品質は最高品質を選んでいますが、待てないほどではないにしろ割と時間がかかるかなという印象です。その分画質はどうなっているか確認してみたいと思います。

左がオリジナル、右が ETC2 圧縮したものですがどうでしょうか。違いはぱっと見た感じでわかるでしょうか?

compare-etc2-sample01
compare-etc2-sample02

個人的には ETC1 のころと違ってよく綺麗に圧縮できているのではないかと思っています。そこで今度はこれらの一部分を拡大して確認してみたいと思います。 続きを読む


PVRTC2の圧縮ノイズを調べてみた


今回は PVRTC2 の圧縮について、どのくらいの劣化が起こるのかを調べてみました。 PVRTC2 って有名なようでイマイチな感じで、これを検索キーワードにしても PVRTC1 の 2BPP モードがヒットする感じです。 PVRTC-I, PVRTC-II という表記の方がいいのかもしれませんが、 imgtec のブログでは PVRTC, PVRTC2 という記述をされているようです。

PVRTC2について

PVRTC2 は PVRTC をさらに拡張した形式となっています。基本的なブロックフォーマットはそのままに、ブロックごとの2色を記している最上位ビットに別の意味を持たせた感じにしています。それぞれに Opaque なビットは不要、他方から判定すればよし!というのは納得がいきます。
この別の意味を付与されたビットを HardBit と呼んでいます。これにより最近流行のブロックのモードを追加するということを実現しており、追加されたモードは Non-interpolated(非補間), Local palette(ローカルパレット) モードの2種類となっています。これらの仕組みから PVRTC2 は画質の向上のために用意されているようです。

参考: PVRTC,PVRTC2についての Imagination Technologies のブログ

実験

以前の BC7 のノイズ検証でも使ったデータを利用して確認してみます。PVRTC2 データの作成ついては、今のところ PVRTexTool で行えます。
このツールは前後でのノイズ(エラー)も表示させることが可能なため、以前のように別アプリで処理しなくても簡単に確認できます。ここではそのスクリーンショットを載せることで見ていきたいと思います。
注意事項としては、 BC7 と PVRTC2 を単純に比較してはいけないということです。お気づきかと思いますが、 BC7 は 8bit per pixel(BPP) であり、 PVRTC2 は 4BPP であるということです。つまり PVRTC2 が BC7 よりも2倍圧縮率が高いということで、すなわち BC7 より見た目が悪くても当然ということです。

pvrtc2-noise-sample01

pvrtc2-noise-sample02

よく言われていることではありますが、PVRTC系はアルファチャンネルも持たせられるがその場合カラーの品質が落ちる、というのが今回も健在のようです。最初の例ではアルファ抜きの部分にもノイズがありますし、アルファが変化するブロックにあるカラーブロックがひどいノイズとなっています。
アルファ不要のケースにおいてはノイズは階調が変わる輪郭部分に大きく発生していますが、等倍の目視ではあまり感じられないかと思います。 続きを読む


BC7の圧縮ノイズについて調べてみた


優秀だという BC7 圧縮の品質がどんなものか調べてみました。
そのためにはそこそこの品質以上の画像データが必要だったので友人に協力してもらい画像を使わせてもらっています。
この場を借りてお礼申し上げます。

BC7への圧縮については Codeplex からダウンロードできる Microsoft の TexConv を使用しています。この最高品質となる CPU で処理した結果ではなく、 GPU を用いた速度とのバランスがとれているであろうモードを選択しています。

比較

いきなりですが圧縮前後の画像を比較してみます。
左が圧縮前、右が圧縮後のデータです。圧縮ノイズなどは確認できるでしょうか?自分では等倍でぱっと見た感じわかりませんでした。心持ち髪の毛曲線部とアルファの部分でノイズが気になるかも程度です。
compare_std_bc7_sample01
compare_std_bc7_sample02

続きを読む


OpenGLでBC7テクスチャ圧縮を使う


今回は今までにやっていそうでやっていなかった OpenGL で圧縮テクスチャを使用する方法です。
基本的には、定番のDXT1,DXT3,DXT5 などの形式をロードする方法と全く同じなのですが、意外と誰もBC6H, BC7 で書いていなかったため、情報として残しておこうと思った次第です。

BC7テクスチャ圧縮について

詳しいことは他のサイトにお任せしますが、BC7はアルファチャンネル付きでも見た目の情報劣化が少ないテクスチャ圧縮形式です。従来はこの領域では DXT5 が使用されましたが、DXT5 は意外と汚いという認識があるようです。BC7はこの点を改善していますが、今のところ圧縮時間がそれなりにかかるのが懸念点となっています。
とはいっても、GPUの支援を使い一定レベルの変換は割と早くできますし、今となっては問題も少ないんじゃないでしょうか

BC7テクスチャをどう作るか

テクスチャを使用するプログラムは難しくないのですが、データを作成するところが現時点では問題になるかと思います。
いくつか方法はあるのですが、ここではマイクロソフトの公開している方法を紹介したいと思います。

DirectXTex プロジェクト一式をダウンロードすると、その中に TexConv というプロジェクトが含まれています。これをVisualStudio で開いて、ビルドするとコマンドラインツールとしての TexConv ができあがります。

詳しいオプションはツールのヘルプを見てほしいのですが、大体以下のような感じで使えます。入力フォーマットもそこそこ対応しているようです。

通常だとGPUが支援として使える状況であればそちらを使うようで、 -nogpu というオプションをつけるとフルにCPUで演算するモードとなります。
どのくらい重い処理なのか一度試してみるのもおもしろいと思います。2048×2048 なデータで試してみると15~20分くらい返ってこなかった気がします・・・。

BC7 形式で dds 出力すると従来の DDS と違い、DX10 用のヘッダも格納されている点に注意して、ロードプログラムを作成します。

プログラム

ロードする際の肝は、従来と変わらずの glCompressedTexImage2D 関数を使用します。気をつけるポイントは前に述べたデータ開始位置が異なることでしょうか。
DDSを識別するマジックナンバー、DDSヘッダ、DX10ヘッダ の分を先頭からずらした位置が BC7データの開始位置となります。

コード例としてはこんな感じになりました。
BC7 をロードする際には、 GL_COMPRESSED_RGBA_BPTC_UNORM_ARB をフォーマットとして設定して glCompressedImage を呼び出します。また本来ならば、DDSのヘッダから画像解像度を取り出して使うべきですが、手抜きして直接 256×256 を指定してしまっています。

OpenGL-BC7

補足

BC6Hの場合には、GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB を指定します。
また、BC7のSRGBが要求されるときには、GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB を使用します。
それ以外はロードの方法に変更はありません。


OpenGLでOut of memory とメモリの関係 (AMD)


今まで NVIDIA のもので調べていたので今回は同じプログラムを AMD Radeon 5450 を使ってデータ取得してみました。

  • Windows 7 x64 (2GB)
  • AMD Radeon HD 5450 (1GB)
  • ドライバは Catalyst Omega 14.12

プログラムは OpenGL を用いて 32MB 単位で頂点バッファを 300 フレーム間隔で確保していくものです。作成後、 Out of memory のエラーが返ってきたら終了します。

実験結果

何度か動かしてみましたが、 GL_OUT_OF_MEMORY のエラーが取得できませんでした。あふれた状態になったと思われるときには、アプリケーションがフリーズ状態に陥りました。 SwapBuffers の実行内部で無限ループしているような気配でした。
 この状況になるまでのデータで、システムの使用メモリ、 VRAM の使用量、共有メモリの使用量をグラフ化してみたものが以下のようになります。 NVIDIA の場合とは傾向が違っていておもしろいです。

radeon_ogl_outofmemory_log
今回の場合は、 VRAM に配置できる場合にはそちらに配置して、システムのメモリは消費しないようにみえます。実際にこのタイミングではプライベートヒープが増えているような感じではありませんでした。 観測タイミング次第によっては glBufferData の中身のテンポラリの DMA 転送タイミングで作業バッファぽいものが見えたりしますが、数フレーム後解放されたりしました。 VRAM に配置できない状況になったときに、初めてシステムのメモリを消費しての共有メモリ使用増加が観測できました。おおよそ 1.6GB ほどの使用量になったときにアプリケーションがフリーズ状態に陥ったように見えます。この環境ではビデオアダプタのプロパティで確認できたのが「利用可能な全グラフィックスメモリ 1791MB」とあり、最大値であるこの値にまで到達したのかなと思っています。

感想

AMD のグラフィックスドライバは余計な(?)システムメモリを消費しなかったので、 OpenGL 実装でも無駄はないという点に関心しました。一方で、メモリあふれのエラーを検知できないのはマズイですね。無駄なメモリは確保しないから、溢れさせないよう気をつけてアプリケーションを実装しましょう、ということでしょうか。

おまけ

同じようなテストを Windows8 (64bit) に切り替えて行ってみました。おおよそ同じ傾向になりました。ただシステムメモリの細かな変動が観測され、作業メモリとして使用している部分がみえたのかなと思っています。(これは以前に glBufferData 直後での内部動作でそのような動きを見せたためです。)
ドライバが標準的に導入されたものを使用し、 Catalyst Omega ではない点ももしかすると影響しているかもしれません。
radeon_ogl_outofmemory_log_win8


OpenGL @NVIDIAでのメモリ調査


NVIDIA Geforce で OpenGL を使った場合のシステムメモリの消費について調べてみましたが、サイズ可変でどのくらい消費量が変わるのか、というものでした。また API 実行の各タイミングで計測したのみで、時間経過を考慮していませんでした。

今回はサイズは固定し、 100 フレームに 1 回頂点バッファを確保して描画するという方法を試してみました。また描画に使用されない場合は処理をされないという可能性を除外するため、次フレーム以降は確保したバッファを用いての描画を行うようにしています。

確保のタイミングで、以前の確保時のデータとどのくらいメモリ使用量が変動するかをグラフ化したものが以下となります。
nvidia_opengl_temporary

結果

NVIDIA の場合、 OpenGL の作業用のメモリとして多めのメモリを使用する傾向にあるようです。しかしフレームの経過とともにその作業メモリを解放も行っているようです(そうでないと差分デルタが減った説明ができない)
多くの場合は、 VRAM 側に確保したメモリサイズと同じ程度のメモリを消費すると考えて良さそうです。


OpenGLでOut of memory とメモリの関係


細かな単位で OpenGL の頂点バッファをたくさん確保していたらこんなエラーに出会いました。ちなみにプログラムの実行タイミング次第では OpenGL で OUT_OF_MEMORY のエラー状態がちゃんと返りました。エラー返す前に異常系に落ちたという感じでしょうか。
nvidia_driver_oom

この症状が出るまでの様子を、使用システムメモリ、使用VRAM量、使用中の共有ビデオメモリ量、との値をグラフにしてみたものが以下となります。

nvidia_ogl_outofmemory_log

実験プログラムについて

Windows 7 x64 (16GB) の環境で、 Geforce GTX750 Ti (2GB) を装着しています。
300 描画フレーム間隔で、 128MB の単位で頂点バッファを確保するプログラムを動作させています。また、確保後は次のフレーム以降、バッファ先頭だけ使ってではありますが描画処理も行っています。

このようにしている背景としては、描画されないデータは処理されないのでは?という点と SwapBuffers が呼ばれない場合、作業メモリが解放されないのではという疑惑があるためです。そのために、描画ループを回しつつ、定期間隔で確保実験するに至りました。

この結果について

実際の搭載 VRAM 量までは順調に VRAM 側にリソースが確保されるようです。それがいっぱいになると共有メモリを使うようになっていくのが見えます。このとき、ドライバのなかでデータの整理が行われるのか、一時的に逆転送など行い共有メモリ量が増加&VRAM側をパージしている様子がうかがえました。
また VRAM に確保されようが、システム全体の使用メモリは増加していくのがわかります(黒線のグラフ)。使用した VRAM リソースの合計は約3GBでした。

実験その後

VRAM の断片化とかあるかなと考えて、バッファのサイズを 32MB に減らして再実験しました。このときアプリケーションもまた 32bit ですが、 LargeAddressAware をオンにして 4GB までのアドレス空間使えるようにしてみました。
 しかしながら、4GB まで使えるようになったというような結果を予想していましたが、3GB付近でエラーとなってしまいました。アプリケーションのアドレス空間の問題ではないようです。エラーが出た際のプロセスの状況を探ってみると、 1.3GB 程度のメモリ使用量となっており、このときプライベートヒープで割としめられていました。そのブロック単位も 32MB ほどなっており、どうやら VRAM リソースとして確保した何かのコピー(もしくは実体の待避?)かと思われます。これらの情報は VMMap を見れば詳細にわかります。

このような状況になってもちゃんと動き続ける今の Windows や NVIDIA のドライバはすごいなぁと関心もした次第です。


OpenGL API とメモリの消費について NVIDIA特別編


どうにも NVIDIA のメモリ消費について、メモリ消費しすぎな感じがあったので追加調査してみることにしました。

32MB VBOの場合(前回の結果)

状態 メモリ(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

そこで今回はサイズを変化させた VBO 2つでどのように変化するか、しないかを見ていきたいと思います。

4MB VBO x2

状態 メモリ(MB) Dedicate(MB) 共有メモリ(MB)
初期化終了後 1023.0 36 14
元データ準備(4MB x2) 1037.9 36 14
glBufferData (1)実行後 1050.8 40 16
glBufferData (2)実行後 1054.8 44 24
元データ破棄 1043.4 44 24
初回描画後 1043.4 45 25

BufferData2回分の内部データとして 16.9MB を消費、定常的には 20.6 MB の消費という結果になりました。VRAM のリソースとしては 4MB x2 = 8MB = 44-36 と想定に合うサイズが使用されています

16MB VBO x2

状態 メモリ(MB) Dedicate(MB) 共有メモリ(MB)
初期化終了後 1019.1 36 14
元データ準備(16MB x2) 1048.2 36 14
glBufferData (1)実行後 52 32
glBufferData (2)実行後 1133.2 68 36
元データ破棄 1101.2 68 36
初回描画後 1101.4 70 37

BufferData2回分の内部データとして 85MB を消費、定常的には 82.2 MB ほどメモリを使用する結果になりました。 VRAM リソースとしては 70-36 = 34 MB となりおおよそ想定の範囲の結果に。

64MB VBO x2

状態 メモリ(MB) Dedicate(MB) 共有メモリ(MB)
初期化終了後 1023.9 36 14
元データ準備(64MB x2) 1156.0 36 14
glBufferData (1)実行後 1302.1 100 32
glBufferData (2)実行後 1433.2 164 36
元データ破棄 1304.9 164 36
初回描画後 1309.3 165 37

BufferData 2回分の内部データとして 277.2MB, 定常状態として 285MB の消費となっています。

まとめ

サイズを変更させて実験してみましたが、元データサイズの倍以上は消費されると考えて良さそうです。
これらの内容をグラフ化すると以下のようになります。なかなか作業メモリ(というかバッキングストア)分がひどいことに。

nvidia_ogl_working_memory_usage

追加

別視点から実験してみました。OpenGL @NVIDIAでのメモリ調査