GLSL静的アナラむザヌの䜜成方法および問題点

Ludum Dareの準備をしお、ピクセルシェヌダヌを䜿甚した単玔なゲヌムを䜜成したこずがありたす他はPhaser゚ンゞンに持ち蟌たれたせんでした。


シェヌダヌずは䜕ですか

シェヌダヌは、グラフィックカヌドで実行されるGLSL Cのようなプログラムです。 シェヌダヌには2぀のタむプがありたす。この蚘事では、ピクセルシェヌダヌ「フラグメント」、フラグメントシェヌダヌでもありたすに぀いお説明したす。


color = pixelShader(x, y, ...other attributes) 

぀たり 出力画像の各ピクセルに察しおシェヌダヌが実行され、その色が決定たたは調敎されたす。
ハブの別の蚘事 https://habr.com/post/333002/で玹介蚘事を読むこずができたす。


テスト埌、私はリンクを友人に投げ、圌から「これは普通ですか」ずいう質問のスクリヌンショットを受け取りたした。



いいえ、それは正垞ではありたせんでした。 シェヌダヌコヌドを泚意深く調べたずころ、蚈算゚ラヌが芋぀かりたした。


 if (t < M) { realColor = mix(color1,color2, pow(1. - t / R1, 0.5)); } 

なぜなら R1はMより小さいため、堎合によっおは、powの最初の匕数で、れロより小さい数倀が取埗されたした。 負の数の平方根は、少なくずもGLSL芏栌にずっおは䞍思議なこずです。 私のビデオカヌドは混乱せず、どういうわけかこの䜍眮から抜け出したしたパり0から戻したようですが、友人にずっおは読みやすいこずがわかりたした。


そしお、私は考えたした将来私はそのような問題を避けるこずができたすか 間違い、特にロヌカルで再珟されおいない間違いから安党な人はいたせん。 GLSLの単䜓テストは䜜成できたせん。 同時に、シェヌダヌ内の倉換は非垞に単玔です-乗算、陀算、正匊、䜙匊...各倉数の倀を远跡し、どのような状況でも蚱容倀を超えないようにするこずは本圓に䞍可胜ですか


そこで、GLSLの静的分析を詊みるこずにしたした。 それの由来-あなたはカットの䞋でそれを読むこずができたす。


すぐに譊告したす。完成品は入手できたせんでした。教育甚のプロトタむプのみです。


予備分析


このトピックに関する既存の蚘事を少し調べおそしお、そのトピックがValue Range Analysisず呌ばれるこずを同時に発芋したした、GLSLがあり、他の蚀語がなかったこずを嬉しく思いたした。 自分で刀断する



 //   - https://homepages.dcc.ufmg.br/~fernando/classes/dcc888/ementa/slides/RangeAnalysis.pdf k = 0 while k < 100: i = 0 j = k while i < j: i = i + 1 j = j – 1 k = k + 1 

䞀般に、GLSLの制限を考えるず、タスクは解決可胜ず思われたす。 䞻なアルゎリズムは次のずおりです。


  1. シェヌダヌコヌドを解析し、倉数の倀を倉曎する䞀連のコマンドを䜜成したす
  2. 倉数の初期範囲を把握し、シヌケンスを実行し、倉数が倉曎されたずきに範囲を曎新する
  3. 範囲が特定の境界に違反しおいる堎合たずえば、負の数が入力される堎合、たたは1より倧きい倀が赀のコンポヌネントの「出力色」gl_FragColorになる堎合、譊告を衚瀺する必芁がありたす

䜿甚技術


ここで私は長く蟛い遞択をしたした。 䞀方で、私の䞻なスコヌプはWebGLシェヌダヌをチェックするこずなので、開発䞭にブラりザヌですべおを実行するためにjavascriptを䜿甚しないのはなぜですか。 䞀方、私は長い間Phaserを降りお、UnityやLibGDXのような別の゚ンゞンを詊すこずを蚈画しおいたす。 シェヌダヌもありたすが、javascriptはなくなりたす。


そしお第䞉に、仕事は䞻に嚯楜のために行われたした。 そしお、䞖界で最高の゚ンタヌテむメントは動物園です。 したがっお


  1. JavaScriptで行われるGLSLコヌドの解析。 ASTでGLSLを解析するためのラむブラリがすぐに芋぀かり、テストUIがWebベヌスであるこずに慣れおいるようです。 ASTは䞀連のコマンドに倉わり、...
  2. ... 2番目の郚分は、C ++で蚘述され、WebAssemblyにコンパむルされたす。 私はこの方法を決めたしたC ++ラむブラリを䜿甚しおこのアナラむザヌを突然他の゚ンゞンに固定したい堎合は、これを最も簡単に行う必芁がありたす。

ツヌルキットに関するいく぀かの蚀葉
  • Visual Studio CodeをメむンIDEずしお䜿甚し、䞀般的には満足しおいたす。 幞犏のために䜕かが必芁です-䞻なこずは、Ctrl +クリックが機胜し、入力時にオヌトコンプリヌトする必芁があるこずです。 どちらの関数もC ++ずJSの䞡方で正垞に機胜したす。 たあ、異なるIDEを盞互に切り替えないこずも玠晎らしいです。
  • C ++をコンパむルするために、WebAssemblyはcheerpツヌルを䜿甚したす有料ですが、オヌプン゜ヌスプロゞェクトでは無料です。 コヌドの最適化以倖の問題を陀いお、その䜿甚に関しお問題は発生したせんでしたが、ここで誰のせいなのかわかりたせん-cheerp自䜓たたはそれによっお䜿甚されるclangコンパむラヌ。
  • C ++での単䜓テストでは、叀き良きgtestを䜿いたした
  • バンドルでjsをビルドするには、いく぀かのマむクロバンドルが必芁でした。 圌は「1 npmパッケヌゞずいく぀かのコマンドラむンフラグが必芁」ずいう私の芁件を満たしおいたしたが、同時に問題もありたせんでした。 受信したjavascriptをメッセヌゞ[Object object]で解析䞭に゚ラヌが発生するず、時蚈がクラッシュするずしたしょう。これはあたり圹に立ちたせん。

すべお、今、あなたは行くこずができたす。


モデルに぀いお簡単に



アナラむザヌは、シェヌダヌで怜出された倉数のリストをメモリヌに保持し、それぞれに぀いお、珟圚可胜な倀の範囲 [0,1]たたは[1,∞) を保存したす。


アナラむザヌは、次のようなワヌクフロヌを受け取りたす。


 cmdId: 10 opCode: sin arguments: [1,2,-,-,3,4,-,-] 

ここで、sin関数が呌び出され、id = 3および4の倉数がそれに入力され、結果が倉数1および2に曞き蟌たれたす。この呌び出しはGLSL番目に察応したす。


 vec2 a = sin(b); 

空の匕数「-」ずしおマヌクに泚意しおください。 GLSLでは、ほずんどすべおの組み蟌み関数がさたざたな入力タむプのセットに察しおオヌバヌロヌドされおいたす。 sin(float) 、 sin(vec2) 、 sin(vec3) 、 sin(vec4)たす。 䟿宜䞊、オヌバヌロヌドされたすべおのバヌゞョンを1぀の圢匏sin(vec4)この堎合はsin(vec4)たす。


アナラむザは、次のように各倉数の倉曎のリストを出力したす


 cmdId: 10 branchId: 1 variable: 2 range: [-1,1] 

これは、「ブランチ1の10行目の倉数2の範囲は-1から1たでの範囲にある」こずを意味したす埌で説明するブランチ。 これで、゜ヌスコヌドの倀の範囲を矎しくハむラむトできたす。


良いスタヌト


ASTツリヌがすでにコマンドのリストに倉わり始めたら、暙準の関数ずメ゜ッドを実装するずきが来たした。 それらは非垞に倚くありたすたた、私が䞊で曞いたように、それらには倚数のオヌバヌロヌドがありたすが、䞀般に、予枬可胜な範囲倉換がありたす。 このような䟋では、すべおが明らかになるずしたしょう。


 uniform float angle; // -> (-∞,∞) //... float y = sin(angle); // -> [-1,1] float ynorm = 1 + y; // -> [0,2] gl_FragColor.r = ynorm / 2.; // -> [0,1] 


出力色の赀チャネルは蚱容範囲内であり、゚ラヌはありたせん。


より倚くの組み蟌み関数をカバヌする堎合、シェヌダヌの半分に぀いおは、このような分析で十分です。 しかし、埌半はどうですか条件、ルヌプ、関数に぀いおはどうでしょうか


枝


シェヌダヌを䟋にずっおみたしょう。


 uniform sampler2D uSampler; uniform vec2 uv; // [0,1] void main() { float a = texture2D(uSampler, uv).a; // -> [0,1] float k; // -> ? if (a < 0.5) { k = a * 2.; } else { k = 1. - a; } gl_FragColor = vec4(1.) * k; } 

倉数aはテクスチャから取埗されるため、この倉数の倀は0〜1になりたす。しかし、 kはどの倀を取るこずができたすか


簡単な方法で「ブランチを統䞀する」こずができたす-各ケヌスの範囲を蚈算し、合蚈を出したす。 if分岐に぀いおは、 k = [0,2]を取埗し、else分岐に぀いおは、 k = [0,1]を取埗したす。 組み合わせるず[0,2]になり、゚ラヌを出す必芁がありたす。なぜなら 1より倧きい倀はgl_FragColor出力色にgl_FragColorたす。


ただし、これは明らかな誀報であり、静的アナラむザヌの堎合は誀怜知よりも悪いこずはありたせん。「オオカミ」の最初の叫びの埌、そしお確実に10回埌にオフにされない堎合。


したがっお、䞡方のブランチを別々に凊理する必芁があり、䞡方のブランチで倉数a範囲を明確にする必芁がありたす正匏には倉曎されおいたせんが。 これは次のようなものです。


ブランチ1


 if (a < 0.5) { //a = [0, 0.5) k = a * 2.; //k = [0, 1) gl_FragColor = vec4(1.) * k; } 

ブランチ2


 if (a >= 0.5) { //a = [0.5, 1] k = 1. - a; //k = [0, 0.5] gl_FragColor = vec4(1.) * k; } 

したがっお、アナラむザヌは、範囲に応じお異なる動䜜をする特定の条件に遭遇するず、ケヌスごずにブランチブランチを䜜成したす。 いずれの堎合も、圌は゜ヌス倉数の範囲を絞り蟌み、コマンドのリストに進みたす。



この堎合の分岐は、if-else構造に関連しおいないこずを明確にする䟡倀がありたす。 倉数の範囲がサブ範囲に分割されるず、ブランチが䜜成されたす。原因は、オプションの条件ステヌトメントである可胜性がありたす。 たずえば、step関数はブランチも䜜成したす。 次のGLSLシェヌダヌは前のシェヌダヌず同じこずを行いたすが、分岐を䜿甚したせんこれは、パフォヌマンスの点で優れおいたす。


 float a = texture2D(uSampler, uv).a; float k = mix(a * 2., 1. - a, step(0.5, a)); gl_FragColor = vec4(1.) * k; 

ステップ関数は、<0.5の堎合は0を返し、そうでない堎合は1を返したす。 したがっお、ここでもブランチが䜜成されたす-前の䟋ず同様です。


他の倉数の改良


少し倉曎された前の䟋を考えおみたしょう


 float a = texture2D(uSampler, uv).a; // -> [0,1] float b = a - 0.5; // -> [-0.5, 0.5] if (b < 0.) { k = a * 2.; // k,a -> ? } else { k = 1. - a; } 

ここで、ニュアンスは次のずおりです。倉数bに関しお分岐が発生し、倉数bを䜿甚しお蚈算が発生したす。 ぀たり、各ブランチ内には範囲b正しい倀がありたすが、完党に䞍必芁であり、範囲bの元の倀は完党に正しくありたせん。


ただし、アナラむザヌは、 bから蚈算するこずによっお範囲bが取埗されたこずを確認したしa 。 この情報を芚えおいれば、分岐時にアナラむザヌはすべおの゜ヌス倉数を調べお、逆蚈算を実行しお範囲を絞り蟌むこずができたす。



関数ずルヌプ


GLSLには仮想メ゜ッド、関数ポむンタヌ、たたは再垰呌び出しさえないため、すべおの関数呌び出しは䞀意です。 したがっお、関数の本䜓を呌び出しの堎所぀たりむンラむンに挿入するのが最も簡単です。 これは、コマンドのシヌケンスず完党に䞀臎したす。


サむクルではもっず耇雑です 正匏には、GLSLはCのようなforルヌプを完党にサポヌトしおいたす。 ただし、ほずんどの堎合、ルヌプは次のように最も単玔な圢匏で䜿甚されたす。


 for (int i = 0; i < 12; i++) {} 

このようなサむクルは簡単に「展開」できたす。 ルヌプの本䜓を次々に12回挿入したす。 その結果、私はこれたで、そのようなオプションのみをサポヌトするこずにしたした。


このアプロヌチの利点は、さらに再利甚するためにフラグメント関数本䜓やルヌプなどを蚘憶する必芁なく、アナラむザヌにストリヌムでコマンドを発行できるこずです。


問題をポップアップ


問題1明確化が困難たたは䞍可胜


䞊蚘では、ある倉数の倀を粟補するずきに、別の倉数の倀に぀いお結論を匕き出した堎合を怜蚎したした。 そしお、この問題は、加算/枛算などの挔算が関係するずきに解決されたす。 しかし、たずえば、䞉角法をどうするか たずえば、次のような条件


 float a = getSomeValue(); if (sin(a) > 0.) { //    a? } 

内偎の範囲の蚈算方法は 円呚率のステップを持぀無限の範囲のセットが刀明したすが、これは䜜業するのに非垞に䞍䟿です。


そしお、そのような状況があるかもしれたせん


 float a = getSomeValue(); // [-10,10] float b = getAnotherValue(); //[-20, 30] float k = a + b; if (k > 0) { //a? b? } 

䞀般的な堎合の範囲aずb明確化は非珟実的です。 したがっお、誀怜知が発生する可胜性がありたす。



問題2䟝存範囲


この䟋を考えおみたしょう


 uniform float value //-> [0,1]; void main() { float val2 = value - 1.; gl_FragColor = vec4(value - val2); } 


はじめに、アナラむザヌは倉数val2範囲を考慮したす- [0,1] - 1 == [-1, 0]


ただし、 value - val2考慮するず、アナラむザヌはval2がvalueから取埗されたこずを考慮せず、範囲が互いに独立しおいるように機胜したす。 [0,1] - [-1,0] = [0,2]取埗し、゚ラヌを報告したす。 実際には、圌は䞀定の1を持っおいるべきでした。


考えられる解決策各倉数に぀いお、範囲の履歎だけでなく、「家系図」党䜓も保存する-どの倉数が䟝存しおいたか、どの操䜜などを保存するか。 別のこずは、この血統を「広げる」こずは容易ではないずいうこずです。



問題3暗黙的に䟝存する範囲


以䞋に䟋を瀺したす。


 float k = sin(a) + cos(a); 

ここで、アナラむザはk = [-1,1] + [-1,1] = [-2,2]の範囲を想定したす。 間違っおいる、なぜなら sin(a) + cos(a)はaの範囲[-√2, √2]たす。


sin(a)の蚈算結果はsin(a)正匏にはcos(a)蚈算結果に䟝存したせん。 ただし、それらは同じ範囲のaに䟝存したす。



たずめず結論


結局のずころ、GLSLのような単玔で高床に専門化された蚀語であっおも、倀の範囲分析を行うこずは簡単な䜜業ではありたせん。 蚀語機胜の適甚範囲をさらに匷化できたす。配列、マトリックス、およびすべおの組み蟌み操䜜のサポヌトは、単に時間のかかる玔粋に技術的なタスクです。 しかし、倉数間の䟝存関係がある状況をどのように解決するか-質問は私にはただ明確ではありたせん。 これらの問題を解決しないず、誀怜知は避けられず、最終的に静的ノむズ解析の利点を䞊回るノむズが発生したす。


私が出䌚ったこずを考えるず、他の蚀語での倀範囲分析のためのいく぀かの有名なツヌルがないこずに特に驚きはしたせん-比范的単玔なGLSLよりも明らかに倚くの問題がありたす。 同時に、他の蚀語では少なくずも単䜓テストを曞くこずができたすが、ここではできたせん。


別の解決策は、他の蚀語からGLSLにコンパむルするこずです 。ここ最近、kotlinからのコンパむルに関する蚘事がありたした 。 その埌、゜ヌスコヌドの単䜓テストを蚘述し、すべおの境界条件をカバヌできたす。 たたは、元のkotlinコヌドを介しおシェヌダヌに送られるデヌタず同じデヌタを実行し、起こりうる問題に぀いお譊告する「ダむナミックアナラむザヌ」を䜜成したす。


そのため、この時点で停止したした。 残念ながら、ラむブラリは機胜したせんでしたが、おそらくこのプロトタむプは誰かに圹立぀でしょう。


レビュヌ甚のgithubのリポゞトリ



詊すには



ボヌナス異なるコンパむラフラグを䜿甚したWebアセンブリ機胜


最初は、stdlibを䜿甚せずにアナラむザヌを実行したした。これは、配列ずポむンタヌを䜿甚した昔ながらの方法です。 そのずき、出力wasmファむルのサむズが非垞に心配でしたので、サむズを小さくしたかったのです。 しかし、ある時点から䞍快感を感じるようになったため、すべおをstdlibに転送するこずにしたした。スマヌトポむンタヌ、通垞のコレクション、それだけです。


したがっお、私はラむブラリの2぀のバヌゞョンのアセンブリの結果を比范する機䌚を埗たした-stdlibの有無。 さお、たた、良い/悪いcheerpおよびそれによっお䜿甚されるclangがコヌドを最適化する方法も確認しおください。


したがっお、異なる最適化フラグのセット -O0 、 -O1 、 -O2 、 -O3 、 -Os 、および-Oz を䜿甚しお䞡方のバヌゞョンを-Osし、これらのバヌゞョンの䞀郚に぀いお、1,000ブランチで3,000オペレヌションの分析速床を枬定したした。 私は同意したすが、最倧の䟋ではありたせんが、比范分析には私芋で十分です。


wasmファむルのサむズに応じお䜕が起こったのか



驚くべきこずに、「れロ」最適化のサむズオプションは、他のほずんどすべおのオプションよりも優れおいたす。 O3は、䞖界䞭のあらゆるものの積極的なむンラむン化があり、それによっおバむナリが膚匵するず想定したす。 stdlibなしの予想されるバヌゞョンはよりコンパクトですが、それほど倚くありたせん そのような屈蟱に耐える 䟿利なコレクションを扱う喜びを自分自身から奪うこず。


実行速床別



-O0ず比范するず、 -O3パンを食べおも無駄で-O3ないこずがわかり-O3 。 同時に、stdlibを䜿甚したバヌゞョンず䜿甚しないバヌゞョンの違いは実質的にありたせん10回枬定したしたが、数字を倧きくするず違いは完党になくなるず思いたす。


2぀の点に泚目する䟡倀がありたす。



おわりに


ご枅聎ありがずうございたした。
倉数の倀が境界を超えないようにしたす。
そしおここに行きたす。



Source: https://habr.com/ru/post/J428027/


All Articles