ちょっぴり熱を出してしまったので一回休み。37℃も無かったけど夏の熱はこわいからな。
SIGGRAPHやってんですねー。というわけで恒例のOpenGLバージョンアップ、OpenGL 4.4が出てました。
仕様書はまだよく見てないんだけど、個々の新機能を詳しく知りたい場合は仕様書より新しく追加されたARB拡張を見ていった方が機能毎にまとまってて楽なんだよね。基本的には追加された機能がARB拡張になってるんで。
あとNVIDIAのGL4.4ベータドライバについでに追加されてた拡張もあるよ。
他にもベータドライバにあるかどうかは知らないけど
この2つがRegistryに追加されてた。しかしもう眠いから読むのはあきらめた。
4.4は急に降って湧いたような機能追加はなくて、今まであった拡張機能を順当に取り込んだりAPIの使い勝手を向上させたりした感じ。 そんなに派手さはないけども意外にいろいろやりやすくなってるんじゃないでしょうか。
TexStorageとかであとでサイズが変えられないテクスチャを作れるようになってたけど、そういやバッファオブジェクトはあとからサイズ変えられるなー変えらんないようにするかー。ということでバッファオブジェクトも作れるようにBufferStorageが追加されました。
あとアクセスモードみたいなのも細かく設定できるみたいだ。
テクスチャをクリアできるようにしたよ!やったー!!
指定したテクスチャの全部または一部を特定のデータで塗り潰せるClearTexImageとClearTexSubImageが追加された。これはとても便利。
GLSLの拡張。layout指定にいろいろ追加された。
いろいろ細かい機能追加だけど、基本的にはバイト単位できっちり指定できてだいぶわかりやすくなった感じがある。
一発で沢山のオブジェクトをbindできるようにした、だけ。
まあたとえば
void glBindTextures(uint first, sizei count, const uint *textures);
こんな関数が追加されてるんだが、これはfirstに指定した場所(たとえばGL_TEXTURE0とか)からcount個数分texturesに入ってるテクスチャオブジェクトをbindするってわけだ。
べつにglBindTexture使えばいいじゃんという話なんだが、まあ一発でできるといろいろ効率いいよねということのようだ。
もちろんテクスチャだけじゃない。
void BindBuffersBase(enum target, uint first, sizei count, const uint *buffers);
void BindBuffersRange(enum target, uint first, sizei count, const uint *buffers, const intptr *offsets, const sizeiptr *sizes);
void BindTextures(uint first, sizei count, const uint *textures);
void BindSamplers(uint first, sizei count, const uint *samplers);
void BindImageTextures(uint first, sizei count, const uint *textures);
void BindVertexBuffers(uint first, sizei count, const uint *buffers, const intptr *offsets, const sizei *strides);
これだけあった。そんなに使うかなぁ……?
クエリ結果(Occulusion QueryとかTransform feedbackの出力プリミティブ数とか実行時間とか)を読み出すのってCPU側でしかできなかったんだけどGPU側で読みたいんよ!ということでバッファオブジェクトにクエリ結果を書き出せるようにした。
GetQueryIndexedする時にquery bufferになんかがbindされてたらそこに書き込んでくれるようだ。まあ実行時間はともかくOcculusion Queryの結果とかTransform feedbackの結果はバッファオブジェクトに欲しいというのはわかるな。conditional renderとかもあるけどさ。
ラップモードに負の方向に一回だけ反転してはじっこで止まるGL_MIRROR_CLAMP_TO_EDGEが追加された。
元になった拡張はだいぶ昔からあったみたいだけどなんで今更?という気がする。
8bitのステンシルテクスチャを作れる。
これでRenderbuffer作らなくて済むね!とか書かれてるんだけどだからどうしたというのかよくわからない。
ちゃんと整数値のテクスチャとして読み書きもできるみたいだけど、だったら普通に整数テクスチャ作ればいいしなぁ。
最近GCNの最適化ガイド的なのを見たら、DEPTH_STENCIL_24_8とか使っても深度バッファに32bitとってステンシル8bitは別に確保するから無駄です残念でしたっ!とかいうことが書いてあって愕然としたので、俺の中でステンシルの株はとても低い。
頂点フォーマットにGL_UNSIGNED_INT_10F_11F_11F_REVが追加された。xyzがそれぞれ11bit・11bit・10bitの符号無し浮動小数点であるようなフォーマット。
あれ、こんな感じの今までなかったんだっけ。整数だけだったか。
頂点属性に使うにはちょっと微妙なフォーマットに見えるなぁ。
テクスチャやサンプラオブジェクトをテクスチャユニットにbindせずに、そのままシェーダのパラメータとして渡してサンプリングできるようになる。
何がうれしいって、まずテクスチャユニットにbindするのめんどくせぇってのを解決してくれるのと、samplerが64bit整数として渡せるようになるのでuniformブロックにつっこんだりattribute…というかin/outで渡したりできちゃったりする。素敵!
ってなんか書いた覚えがあると思ったら元になったNV_bindless_textureも書いてましたな。
テクスチャユニットにくっつけなくていいかわりにサンプリングするテクスチャはMakeImageHandleResidentARBで常駐させておく必要がある。こうなるとそのうちGPU側からテクスチャを常駐させる方法が出てきそうですね。
Compute Shaderまだいじってないからよくわかってないんだよなー。
従来はComputeShaderのグループサイズとかがシェーダ内に書き込んであったため固定だったのを、可変にして実行開始時に指定するようなこともできるようにした。とのこと。まあ言ってる意味はわかるんだがComputeShaderいじってないからどのくらいうれしいのかよくわからない。
MultiDrawElementsIndirectって描画回数をCPU側から指定しないといけないけど、indirect bufferをシェーダ側で生成すると何個入ってるかCPU側で取得するのめんどくね?という欠点があった。indirect bufferはシェーダで生成できるのが利点なのに結局回数はCPUで指定しないといけないのかよ!と。
そこで回数もバッファオブジェクトに入れれば、シェーダで回数をバッファオブジェクトに入れてCPU側では何個入ってるか知らんまま描画開始できるよねー。ということでparameter bufferというバッファオブジェクトを作ってここに入った回数分だけ描画するMultiDrawElementsCountというAPIが追加された。最初からやっとけよ感がありますな。
以前にシームレスキューブマップって機能が追加されてたんだけど、コンテキスト全体でON/OFFしかできなかったので、一部のキューブマップは面を跨いで補間してほしくないのになーと思ってもできなかった。 かわりにあたらしくサンプラのパラメータとしてシームレスサンプリングのON/OFFを追加してテクスチャ単位で指定できるようになった。
たしかに以前全体の設定なんだー?とちょっぴりだけ不思議に思った覚えはあるものの、面を跨いで補間はしてほしくないキューブマップって何に使うのかよくわからない。テクスチャ配列ではいかんものか。
頂点シェーダには処理中の頂点番号が入るgl_VertexIDと処理中のインスタンス番号が入るgl_InstanceIDがある。 これに入ってくる値ってのがgl_VertexIDにはglDrawElementsInstancedBaseVertexBaseInstance(ひでえ長さの関数名)のbaseVertexに指定した値が加算されたものなんだが、gl_InstanceIDにはbaseInstanceに指定した値が加算されてないものが入ってくるようだ。 理由はあるもののさすがにそりゃひでぇ。かといっていきなり変数の意味を変えるわけにいかないのでgl_BaseVertexとgl_BaseInstanceの2つを追加しておいたのであとはよしなんしておくんなせぇということ。
あとついでにMultiDraw~で今何番目なのかわかるgl_DrawID変数も追加しておいたと。今までなかったんだー。
なんともよくわからん、というか変な最適化用の機能。
シェーダで、
if (はしょれますか) {
result = はしょった計算();
}
else {
result = まじめな計算();
}
こんな分岐をした時にシェーダ実行毎(たとえばピクセル毎)にはしょれますかがtrueだったりfalseだったりばらつくとどういう動きをするかっつーと、ハードウェアの都合ではしょった方とまじめな計算の両方を実行してから書き込みマスクで振り分ける、ということをやっちゃったりする。GPUさんは細かい分岐が苦手なのでいっそ両方実行しちまった方が楽なんだよ!と。 いやしかしはしょれる時は速くしようと思って気を効かせてみたのに、両方実行されてはまじめな計算するより遅くなって本末転倒ではないですかと。
こんな時どうするかというと、両方実行するくらいなら全部まじめな計算だけを通しちゃった方が両方実行するよりは速いわな。というのを実現する機能を追加した。
if (allInvocations(はしょれますか)) {
result = はしょった計算();
}
else {
result = まじめな計算();
}
みんな揃ってはしょれますかがtrueになるとallInvocations(はしょれますか)がtrueを返してはしょった計算のみ通る。そうでなければallInvocations(はしょれますか)はみんな揃ってfalseを返すのでまじめな計算のみ通る。結果として両方通ることはなくなって、最悪でもまじめな計算より遅くなることはなくなってめでたしめでたし。
他には誰かがtrueを返していればtrueになるanyInvocationsと、値がみんな同じでさえあればtrueになるallInvocationsEqualというのもある。
なんというか、ハード依存すなぁ……。 両方実行されちゃったということをどうやって知ればいいんでしょうか。デバッガ?
仮想的にでーーーーーーっかいテクスチャを作りつつも一部だけ実メモリに載せといて扱えるようにする。
テクスチャのパラメータとしてGL_TEXTURE_SPARSEをGL_TRUEに設定したあとにglTexStorage~ですごくでかいテクスチャを作ってみると、サイズだけ設定されつつ実メモリは全く確保されていないすかすかテクスチャができあがる。
しかしメモリが確保されてないすかすかのままだとデータの読み書きできなくて意味ないんで、glTexPageCommitmentっつー新しい関数ででかいテクスチャの一部のこっからここまでを実際に使いますよと宣言してやる(commit状態にする)。するとそこの分だけメモリが確保されてあとはglTexSubImageで書き込むなりレンダリングしてみたり、普通にテクスチャとして読み込んだりできるようになる。その部分がいらなくなったらまたglTexPageCommitmentでuncommit状態にするとメモリが解放される。という感じで簡単にでっかいテクスチャ一枚の必要なところだけ使えるようになるというわけだ。
uncommit状態の場所を読み書きしたら爆発するのかと思ったが、読み出しこそ不定値なものの、ちゃんと書き込みは単に無視されるようで安心である。
AMD_sparse_textureと同じかと思ったが、AMD_~の方はcommit状態にするのに特別な関数がなくてglTexSubImageとかでデータを書き込むとcommit状態になって、glTexSubImageのデータにNULLを渡すとuncommit状態になるとかわけのわからんことが書いてあったので、だいぶわかりやすくなっててよかった。
無駄にメガテクスチャとかやりたくなる機能だわ。
ちょっと気になるのはHW依存らしき仮想ページサイズとかいうのがあって、テクスチャの幅・高さ・深さはその倍数にしないといけないようだ。 HW依存てのがめんどそうね。
これはNV拡張なんでGL4.4関係ないけど、NVIDIAのGL4.4ベータドライバに入ってるようなのでついでに。
ブレンドモードを超拡張。 ブレンド式の表を見るだけで頭おかしいだろという気持ちになります。以下引用。
Mode Blend Coefficients
-------------------- -----------------------------------
ZERO (X,Y,Z) = (0,0,0)
f(Cs,Cd) = 0
SRC_NV (X,Y,Z) = (1,1,0)
f(Cs,Cd) = Cs
DST_NV (X,Y,Z) = (1,0,1)
f(Cs,Cd) = Cd
SRC_OVER_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = Cs
DST_OVER_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = Cd
SRC_IN_NV (X,Y,Z) = (1,0,0)
f(Cs,Cd) = Cs
DST_IN_NV (X,Y,Z) = (1,0,0)
f(Cs,Cd) = Cd
SRC_OUT_NV (X,Y,Z) = (0,1,0)
f(Cs,Cd) = 0
DST_OUT_NV (X,Y,Z) = (0,0,1)
f(Cs,Cd) = 0
SRC_ATOP_NV (X,Y,Z) = (1,0,1)
f(Cs,Cd) = Cs
DST_ATOP_NV (X,Y,Z) = (1,1,0)
f(Cs,Cd) = Cd
XOR (X,Y,Z) = (0,1,1)
f(Cs,Cd) = 0
MULTIPLY_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = Cs*Cd
SCREEN_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = Cs+Cd-Cs*Cd
OVERLAY_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = 2*Cs*Cd, if Cd <= 0.5
1-2*(1-Cs)*(1-Cd), otherwise
DARKEN_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = min(Cs,Cd)
LIGHTEN_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = max(Cs,Cd)
COLORDODGE_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) =
min(1,Cd/(1-Cs)), if Cs < 1
1, if Cs >= 1
COLORBURN_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) =
1 - min(1,(1-Cd)/Cs), if Cs > 0
0, if Cs <= 0
HARDLIGHT_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = 2*Cs*Cd, if Cs <= 0.5
1-2*(1-Cs)*(1-Cd), otherwise
SOFTLIGHT_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) =
Cd-(1-2*Cs)*Cd*(1-Cd),
if Cs <= 0.5
Cd+(2*Cs-1)*Cd*((16*Cd-12)*Cd+3),
if Cs > 0.5 and Cd <= 0.25
Cd+(2*Cs-1)*(sqrt(Cd)-Cd),
if Cs > 0.5 and Cd > 0.25
DIFFERENCE_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = abs(Cd-Cs)
EXCLUSION_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = Cs+Cd-2*Cs*Cd
INVERT (X,Y,Z) = (1,0,1)
f(Cs,Cd) = 1-Cd
INVERT_RGB_NV (X,Y,Z) = (1,0,1)
f(Cs,Cd) = Cs*(1-Cd)
LINEARDODGE_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) =
Cs+Cd, if Cs+Cd<=1
1, otherwise
LINEARBURN_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) =
Cs+Cd-1, if Cs+Cd>1
0, otherwise
VIVIDLIGHT_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) =
1-min(1,(1-Cd)/(2*Cs)), if 0 < Cs < 0.5
0, if Cs <= 0
min(1,Cd/(2*(1-Cs))), if 0.5 <= Cs < 1
1, if Cs >= 1
LINEARLIGHT_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) =
1, if 2*Cs+Cd>2
2*Cs+Cd-1, if 1 < 2*Cs+Cd <= 2
0, if 2*Cs+Cd<=1
PINLIGHT_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) =
0, if 2*Cs-1>Cd and Cs<0.5
2*Cs-1, if 2*Cs-1>Cd and Cs>=0.5
2*Cs, if 2*Cs-1<=Cd and Cs<0.5*Cd
Cd, if 2*Cs-1<=Cd and Cs>=0.5*Cd
???
HARDMIX_NV (X,Y,Z) = (1,1,1)
f(Cs,Cd) = 0, if Cs+Cd<1
1, otherwise
Table X.2, Advanced Blend Equations
いやこれブレンド式っつーレベルじゃないですよね。ああこいつらGL拡張だけでFlashとか描画できるようにしようとしてやがるよ……。
あとこれだけじゃなくてHSLブレンドモードとか一貫性のあるブレンドとかもうちょっといろいろと激しいの追加されてますが真面目に読むのめんどいので略。 気になる人は自分で読んでください。