月別アーカイブ: 2012年8月

OpenGL ES 3.0 と Shader Model の関係、まとめ wiki の更新

OpenGL ES 3.0 の API はおよそ 100 個増えています。
OpenGL ES 2.0 で 150 弱だった命令数が 250 個になりました。
シェーダーと世代の対応は下記の通り。

OpenGL         OpenGL ES        DirectX         ShaderModel
---------------------------------------------------------------
OpenGL 1.x     OpenGL ES 1.x    Direct3D 7      Fixed Pileline
OpenGL 2.x     OpenGL ES 2.0    Direct3D 9      Shader Model 3
OpenGL 3.x     OpenGL ES 3.0    Direct3D 10     Shader Model 4
OpenGL 4.x     --               Direct3D 11     Shader Model 5

OpenGL ES 3.0 は OpenGL 3.x 同様に DirectX10 世代、ShaderModel 4.0 に
相当します。

ハードウエアも API も順当に進化しています。
GPU 毎に機能差は生じますが、下記はそれぞれ最小値の比較です。

                            GLES 2.0   GLES 3.0  (DirectX)
-----------------------------------------------------------------
Vertex Uniform Vectors        128        256     (VSH Constant)
Fragment Uniform Vectors       16        224     (PSH Constant)
Vertex Attribs                  8         16     (VSH Input)
Vertex Output Vectors           8         16     (VSH Output)
Fragment Input Vectors          8         15     (PSH Input)
Draw Buffers                    1          4     (PSH Output,MRT)
Vertex Texture Image Units      0         16     (Vertex Texture)
Texture Image Units             8         16     (Pixel Texture)
Combined Texture Image Units    8         32

Uniform Block (D3D の ConstantBuffer) があるためこのあたりの制限は
さらにゆるくなるはずです。

このあたりを踏まえて wiki もいろいろ更新しました。

Mobile GPU 毎の機能比較表
OpenGL のバージョン表など
テクスチャフォーマットまとめ
Desktop 向け OpenGL ES 2.0 / OpenGL ES 3.0 実行環境

OpenGL ES 3.0 には GeometryShader がありませんが、これは OpenGL 3.0 も
同様でした。
GeometryShader が追加されたのは OpenGL 3.2 からなので、
同じように OpenGL ES にも今後 GeometryShader が追加拡張される
可能性が高いといえます。

関連ページ
OpenGL まとめトップ

OpenGL ES 3.0 と OpenGL ES 2.0 の互換性

OpenGL ES 3.0 は OpenGL ES 2.0 との上位互換性が保たれています。
まだ GL 4.3 の GL_ARB_ES3_compatibility と Mali/Adreno Emulator で
試しているだけですが、OpenGL ES 3.0 上で今までのソースコードも
シェーダーも 2.0 のまま変更なしに動いています。
OpenGL ES 3.0 は OpenGL ES 2.0 の実行環境の一つとして利用できそうです。

OpenGL ES 3.0 対応のため EGL の初期化は若干変更しています。
EGL_CONTEXT_CLIENT_VERSION に 3 を渡しています。
Mali のドキュメントでは EGL_OPENGL_ES3_BIT を使っていますが、
定義が見つからなかったため EGL_OPENGL_ES2_BIT のまま使用しました。

GLSL のシェーダーバージョン対応は下記のとおりです。

                 GLSL     shader 内の指定
-----------------------------------------------------------
OpenGL ES 2.0    1.0      #version 100
OpenGL 2.0       1.1      #version 110
OpenGL 2.1       1.2      #version 120

OpenGL 3.0       1.3      #version 130
OpenGL 3.1       1.4      #version 140
OpenGL 3.2       1.5      #version 150 [core/compatibility]
OpenGL ES 3.0    3.0      #version 300 es
OpenGL 3.3       3.3      #version 330 [core/compatibility]
OpenGL 4.0       4.0      #version 400 [core/compatibility]
OpenGL 4.1       4.1      #version 410 [core/compatibility]
OpenGL 4.2       4.2      #version 420 [core/compatibility]
OpenGL 4.3       4.3      #version 430 [core/compatibility/es]

番号が重なっていないためバージョンの数値だけでもすべて区別可能です。
shader の 1行目に #version が存在しない場合、OpenGL では 1.1 とみなします。
OpenGL ES では 1.0 とみなすことになっています。

バージョン番号の後に profile を指定できるようになったのは 1.5 からです。
OpenGL 4.3 では core, compatibility の他に es も加えられています。
実際に OpenGL 4.3 の context のまま “#version 300 es” を指定しても
コンパイルは通りました。

OpenGL 3.0 以後と以前でシェーダーの書き方が変わります。
OpenGL ES 3.0 も OpenGL 3.x 以降と同じ記述方法になりました。
共存させるために下記のようなヘッダを用いています。

// Vertex Shader
#ifdef GL_ES
# define LOWP       lowp
# define MEDIUMP    mediump
# define HIGHP      highp
precision HIGHP float;
#else
# define LOWP       
# define MEDIUMP    
# define HIGHP      
#endif

#if __VERSION__ < 130
# define IN         attribute
# define OUT        varying
#else
# define IN         in
# define OUT        out
#endif
// Vertex Shader
#ifdef GL_ES
# define LOWP       lowp
# define MEDIUMP    mediump
# define HIGHP      highp	
precision MEDIUMP float;
#else
# define LOWP
# define MEDIUMP
# define HIGHP
#endif

#if __VERSION__ < 130
# define IN     varying
# define        out_FragColor  gl_FragColor
#else
# define IN     in
out vec4        out_FragColor;
#endif

上記以外に texture のサンプリング命令も変わります。

iOS CADisplayLink とフレームレート

DisplayLink の描画呼び出しは基本的に 1/60 sec 単位で行われます。

わかりやすく言えば WaitVSync です。
処理が落ちが発生した場合も、次の VSync 相当のタイミングを待ってから
呼び出しが行われています。

そのため問題となっていたのが frameInterval を 2に設定した場合で、
当初この API で呼ばれるタイミングを勘違いしていました。

        (A)     (B)     (C)     (D)
--------------------------------------------
frame 0 draw    draw    draw    draw
frame 1
frame 2 draw
frame 3         draw    draw
frame 4 draw            draw    draw
frame 5         draw
frame 6 draw            darw    darw

(A) = 処理落ちなし
(B) = 処理落ちした場合の実際の動作
(C)/(D) = これは間違い

基本的に 2 frame に一度呼ばれますが、処理落ちが発生した場合は
次の VSync 相当のタイミングで呼ばれます。
上の (B) の動作となり、(C) や (D) にはならないようです。

OpenGL 4.3/ES 3.0 ETC2 Texture 圧縮の仕組み (PVRTC2,ASTC)

DXT(S3TC/BC) 等、テクスチャ圧縮は基本的に 4×4 pixel 単位で行います。
DXT1~5 (BC1~3) では代表色 2色を RGB565 で格納し、その補間値
3~4 level に対して 2bit の index を持ちます。
これを 4×4 = 16 pixel 分格納します。

代表色 2色   16bit x 2色     = 32bit
Index         2bit x 16pixel = 32bit

16pixel 分の情報が 64bit で収まります。

DXT1(BC1)    64bit    4bpp
RGB565      256bit   16bpp
RGB888      384bit   24bpp

1pixel あたり 2bit の選択しか出来ないので、4×4 block 単位で最大
4種類のカラー値に変換していることになります。
これはその他のテクスチャ圧縮フォーマットでもほぼ同等の制約です。

色変化の差が激しい画像など、4種類のカラー値で表現しきれない場合は
ブロックノイズとして目立つことになります。
これらの欠点を無くするために、後発のフォーマットでは様々な工夫が
行われています。

高画質化のための方針の一つがモード分岐です。
対象とする画像に応じてブロック単位に圧縮アルゴリズムそのものを
変えてしまうわけです。

特に、以前解説した BC6H/BC7 (OpenGL では BPTC) 圧縮フォーマットに
至っては、8または 14 通りものモード分岐が存在しています。
block 分割 mode を含めれば種類はもっと多くなります。

Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (1)
Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (2)

Mobile 系 GPU 、Android 等で使われている ETC1 では、各ブロックをさらに
4×2 のサブブロックに分割します。
各ブロック単位で代表色 1色 + 4段階補間が可能なので、4×4 単位で 8種類の
カラー値を持つことができます。

その代わり色解像度は 4×2 ブロック単位となるため、元画像よりもおよそ
1/8 に低下します。分割方向は block 単位で縦横選べますが、YUV のように
色情報を 4×2 block で共有しているようなものです。

そのため色変化が激しい画像では 4x2block の大きなノイズが生じます。
特にはっきりとした 2色の境界線では ETC1 の劣化が目立ちます。
DXT1 の方がずっと綺麗に出ます。 (2012/08/23追記 実例を載せました)

OpenGL ES 3.0 ではこれらの欠点を克服するために ETC2 が採用されました。

● ETC2 の改善点

(1) ETC1 の block ノイズが出やすいケースを軽減、高画質化
(2) DXT5/3DC-X/3DC-XY (BC3/BC4/BC5) のようなフォーマットバリエーションの追加

(1) 高画質化

ETC1 に対するモード分岐の追加です。
もともとカラー値には 2通りの格納方法がありましたが合計 5 mode 分岐に
増えました。

ETC2 の特徴の一つが ETC1 の上位互換で、bit フォーマットは完全に
互換性があります。
ETC1 の差分 mode のオーバーフローを利用して、存在し得ない bit 並び
になたっときに ETC2 とみなします。

ETC1  444(x4 LV) + 444(x4 LV)      table 3+3 = 30+32= 62bit (4x2 block)
ETC1  555(x4 LV) + diff 333(x4 LV) table 3+3 = 30+32= 62bit (4x2 block)
ETC2  444(x3 LV) + 444             dist  3   = 27+32= 59bit (T-Mode)
ETC2  444(x2 LV) + 444(x2 LV)      dist  3   = 27+32= 59bit (H-Mode)
ETC2  676 + 676 + 676                        = 57bit        (Planar)

ETC1 の上 2つは 4×2 サブブロック分割があり、分割方向を選ぶことができます。
ETC2 の T-Mode/H-Mode はサブブロック分割がありません。
そのため 4×4 block 内で 4値の選択となります。

ETC2 の最後の Planar は 4×4 block で 3色を直に格納します。
index がなく単純なグラデーションのみとなります。
ポリゴンの頂点カラーをグーロー補間するような感じです。

まだ実際に画像で試していませんが、サブブロック分割が無くなったため
斜め線など 2色の境界は表現できるようになっているようです。

(2) フォーマットバリエーションの追加

ETC1 は RGB しか格納できず alpha カラー値が含まれる場合 GPU
固有の圧縮フォーマットを用いる必要がありました。
ETC2 は 単チャンネル圧縮フォーマット EAC との組み合わせで DXT5(BC3)
のような使い方ができます。

ETC2 RGB                 4bpp    DXT1/BC1
ETC2 RGB + Alpha 1bit    4bpp    DXT1/BC1
ETC2 RGB + Alpha EAC     8bpp    DXT5/BC3
EAC                      4bpp    3DC-X/BC4
EAC + EAC                8bpp    3DC-XY/BC5

DXT1 は Alpha 1bit のあり無しを 4×4 block 単位で選ぶことができました。
ETC2 では別フォーマット扱いとなります。
ETC1 互換モードの mode 選択 bit を流用するため ETC1 時に 1 mode 減ります。
EAC は BC4/5 同様 pixel あたり 3bit の情報を持ち、8段階選択できます。
ただしベース値を元に 11bit 相当の補間が可能となっているようです。

●その他の新テクスチャフォーマット

・PVRTC2 (PVRTCII)

ETC2 同様に PVRTC とほぼ互換性を保ちつつ、ほとんど使われることが
なかった bit を利用してモード分岐が追加されています。
4 mode あるようですが詳しい構造はわかりません。
すでにツールがあるので比較できますが、従来苦手だった画像でも良くなっており
かなり画質向上が見込めるようです。

・ASTC

ETC2 とそのバリエーションによって OpenGL ES 3.0 では S3TC(DXT) BC1~5
相当をカバーできるようになりました。
ASTC はさらにその上を狙ったもので、HDR対応などを考えるとおそらく
BC6H/BC7 (BPTC) の対抗フォーマットではないかと思います。

圧縮サイズの単位が 128bit なのも BC6H/BC7 (BPTC) と同じです。

block 圧縮のテクスチャフォーマットの方向性の一つがモード分岐の
増加ですが、ASTC はさらにブロックサイズが可変となりました。

PVRTC の 2bpp mode は、通常 4×4 サイズであるブロックを 8×4 に拡張する
ことで実現しています。8×4 = 32block では index が足りないため、
実質 32pixel で 2色の選択になります。

ASTC では可変 block サイズを汎用化しており、mode 分岐だけでなく
より効率の良い 8bpp 未満の圧縮率も同時に実現しているようです。

後ほどもう少し詳しく調べてみたいと思っています。

(2012/08/23追記 :OpenGL 4.3/GLES 3.0 次の圧縮テクスチャ ASTC)

関連エントリ
Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (2)
Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (1)
OpenGL の圧縮テクスチャ (3) BPTC, BC6H/BC7
OpenGL の圧縮テクスチャ (2) 法線圧縮
Android OpenGL ES 2.0 の圧縮テクスチャ

Xcode 4.4 と Objective-C property

知人がはまっていたバグ。

@interface MyClass : NSObject {
  MyObj* p;
}
@property (retain) MyObj* p;
@end

@implementation MyClass
@synthesize p;
-(void)setp
{
   p= value;  // ← ここ

   ~
   self.p= nil;
}
@end

ようするに Property と思って代入したのに “self.” が足りなかった
ため retain されていませんでした。

1. p= value;
2. self->p= value;
3. self.p= value;

どれもコンパイルが通りますが上の 2つはメンバへの直接アクセスで、
3. だけ property のメソッドが呼ばれます。

Xcode 4.4 以降はメンバの宣言も @synthesize も書かなくて良くなっています。
簡単に書けますし、この時デフォルトで挿入される変数も “_p” となるため、
上の 1. 2. のようなミスもエラーで分かります。

// Xcode 4.4 以降

@interface MyClass : NSObject
@property (retain) MyObj* p;
@end

@implementation MyClass
-(void)setp
{
  self.p= value;
  ~
}
@end

次からは絶対 ARC にするともいってました。