「error LNK2001: 外部シンボル "_fltused" は未解決です」を解決する
小さいexeを作っているときに _fltused
が未解決だからどーのというエラーが出てしまってビルドできなくなってしまいました。。
ググると↓のページを見つけて
コスミー報告書[社外秘] - CSTLを使ってDLLを小さくするっていう話
EXTERN_C int _fltused = 0x9875;
を適当な位置に挿入すると動くようになった
windowsのchromeでglslのコンパイルが遅すぎるのでANGLEを切る
最近shadertoyでシェーダーを書いてるんですが
かなり肥大化してきて、コンパイルに20秒ぐらいかかる & たまにブラウザがクラッシュするという問題が発生していました
windowsのchromeはOpenGL ESがANGLE経由で動いているのでglslコードがhlslコードに変換されたのちにコンパイルされているから遅いんだろうなぁという何となくな予想はしていたんですが、特に解決策も調べずに諦めていました
今日会社でなんとなく呟いてみると同僚の方にGPUのドライバが対応していればchromeの起動オプションでANGLEを切れると教えてもらったので試してみました
そのオプションは --use-angle=gl
です
設定した状態で起動してシェーダーをコンパイルしてみると爆速になっていました!うれしい
ただ少し気になることがあって、ANGLEを切った後と切る前でシェーダーの挙動が異なっていました。ANGLEの状態が想定していた挙動でした。
原因は僕が clamp
の第一引数と第三引数が入れ替わった状態で実装していだからだったんですが、なんで今までちゃんと動いてたんだろう...
ちなみにANGLE経由の方がシェーダーの実行速度は早かったです
crinklerとvisual studio 2017で400byteのexeを作る
最近4kbデモを作りたくてグラフィック面でいろいろ勉強していたんですが、今週は小さい実行ファイルを生成する手法を学びました
開発環境
- visual studio 2017 comunity
- crinkler20a
空のプロジェクトを作成する
最近めっきりvisual studioを触る機会がなかったので知らなかったのですが、win32アプリケーションプロジェクトの作成の仕方が変わっていてびっくりしました
プロジェクト新規作成から【Visial C++>Windowsデスクトップ>Windows デスクトップ ウィザード】でプロジェクト名を設定してOK
次のダイアログで
- Windowsアプリケーションを選択
- 空のプロジェクトをチェック
- セキュリティ開発ライフサイクルのチェックを外してOK
crinklerを入手する
以下のURLよりcrinklerを入手します
解凍して得た crinkler.exe
を link.exe
にリネームして、上記で作成した空プロジェクトのソリューションディレクトリに移動します
こちらはデモシーナー界隈でよく使用されているらしいリンカーで、既存のリンカーと差し替えるだけでファイルサイズを圧縮してくれる優れものだそうです
プロジェクトの設定をする
以下の設定をRelease構成で行います
- 【全般>プログラム全体の最適化】を
プログラム全体の最適化なし
に変更 - 【VC++ディレクトリ>実行可能ファイルディレクトリ】の先頭に
$(SolutionDir)
を追加- 先頭に追加しないと
link.exe
を検索する優先度が既存の物に負けてしまってcrinklerが利用されないので気を付けてください(2時間ぐらい潰されました)
- 先頭に追加しないと
- 【リンカー>入力>すべての既定のライブラリを無視】を
はい
に変更 - 【リンカー>マニフェストファイル>マニフェストの生成】を
いいえ
に変更 - 【リンカー>コマンドライン>追加のオプション】に
/CRINKLER
を追加
main.cppを作成してビルド!
main.cppを作成して以下のコードを入力したらリリースビルドしましょう
int WinMainCRTStartup() { return 0; }
424byteの実行ファイルが作成できました! ちなみにcrinklerなしでビルドすると1,536byteなので1/4弱削減できているみたいです。すごいですね
ここからは映像を表示したり、音楽を再生したりする機能を追加してくことになります
いろいろ見ていると機能追加でだいたい1.5kbぐらいになるみたいですね、使える容量は2.5kbしかないのか...
モザイクフィルタを作ってみたところ不可解な境界線が現れてしまいました
画像をモザイクにしようと思ってこんなシェーダーを作成していました
uniform sampler2D tex; void main() { vec2 uv = floor(texCoord * 30.0) / 30.0; gl_FragColor = texture2D(tex, uv); }
実際に使ってみると↓のような見た目になり、なにやらモザイクのセル間に謎の境界線が...
UVを表示してペイントのスポイトで問題の箇所のUVがおかしくなっていないか確認したが問題は見つかりませんでした
実装はどう考えても問題なかったので、それ以外の要因があると2時間ほど頭を抱えていたところ、原因はミップマップがonになっていたからでした
モザイク処理したことによってセル間で急激にUVが切り替わり、境界線でミップマップレベルが大きくなっていたようです
ミップマップ off後
ミップマップには気を付けましょう
シェーダーで条件分岐は遅いらしいのでベクトル演算に置き換えようとしてできなかった話
一般的にシェーダーでの条件分岐は遅いという話がある
今回作ったシェーダーではブレンドモードや使用するマスクのチャンネルをアプリから送信してシェーダー内で利用するということをした
これを条件分岐を使って素直に実装するとこんな感じになる(空で書いたので動くか不明)
fixed3 blendColor(fixed3 src, fixed4 dest, float blend, int mode) { float alpha = dest.a * blend; fixed3 blended_dest = dest.rgb * alpha; if (mode == 0) { return lerp(src, dest, alpha); } else if (mode == 1) { return src + blended_dest; } else if (mode == 2) { return src - blended_dest; } else if (mode == 3) { return src * blended_dest; } return src; } fixed useMask(fixed4 mask, int use_mask_number) { if (use_mask_number == 0) { return 1.0 - mask.r; } else if (use_mask_number == 1) { return 1.0 - mask.g; } else if (use_mask_number == 2) { return 1.0 - mask.b; } else if (use_mask_number == 3) { return 1.0 - mask.a; } return 0.0; }
これの mode
や use_mask_number
を使用したいチャンネルが1.0のベクトルで渡すことで、if文を行列とベクトル演算に置き換えることができる
fixed3 blendColor(fixed3 src, fixed4 dest, float blend, float4 mode) { float alpha = dest.a * blend; fixed3 blended_dest = dest.rgb * alpha; // GLESでは非正方行列が使用できないので使わないところは0で埋める return mul(mode, fixed4x4(lerp(src, dest, alpha), 0, src + blended_dest, 0, src - blended_dest, 0, src * blended_dest, 0)).rgb; } fixed useMask(fixed4 mask, float4 use_mask_vector) { return 1.0 - dot(mask, use_mask_vector); }
これで高速化できただろう!と思ってましたが、結局最初に書いたifのパターンの方がパフォーマンスが出てました。
こちらのリンク先のとおりであれば、実行中に実際に分岐されない場合ではifの方がパフォーマンスがでるんじゃないかと思いました。(未検証)
But on modern GPUs, they don't cause a performance issue unless they actually do diverge at runtime.
またピクセル毎に分岐するような場合はDivergent分岐と呼ばれるらしくて、こういう時にifのパフォーマンスが悪くなるらしい(uvで偶数奇数を分岐するなど)
ただし、この場合でも算術演算によるブランチングがifと比べて速くなるかは検証していないので不明...
(想像の話しかしてないのでアセンブリ読んでみたいけどどこから参照すればいいのかわからない...)
Unityのマテリアルエディタ拡張でテクスチャのプロパティを表示する
最近Unityを触っていてマテリアルエディタを拡張したくなったのでテクスチャのプロパティを表示するところから始めてみた
最初は自前でUIを組み立ててどうやらするのかと思ってその方面で調べていたけど、標準のテクスチャプロパティのスタイルであれば TextureProperty
関数を呼ぶだけで表示できることが分かったのでそれを使用した
using UnityEngine; using System.Collections; using UnityEditor; public class CardShaderInspector : MaterialEditor { public override void OnInspectorGUI() { if (!isVisible) { return; } MaterialProperty mask1 = GetMaterialProperty(targets, "_Mask1Tex"); // 第三引数をtrueにするとタイリングとオフセットのUIも表示される TextureProperty(mask1, "Mask1", false); } }
ここに行き着くのに二時間もかかってしまった・・
windowsにgccをインストールするメモ
go-glというgolangのopenglラッパーをgo getしようとしたところ、cgoを利用しているようでgccが必要という旨が表示されたのでインストールした
MSYS2/MinGW-w64 (64bit/32bit) インストール手順 メモ · GitHub こちらをみて64bitMsys2インストール
pacman -Syuu pacman -S base-devel pacman -S mingw-w64-x86_64-toolchain
このコマンドで開発環境インストール 途中のインストールを指定するところはALLにした
C:\msys64\mingw64\bin
上記にgcc等開発ツール一式がインストールされるので、こちらを環境変数のPATHに加えた
そのあとgo get -uすれば問題無くインストールできた