エンジンと高速化
このプラグインには複数の実行エンジンがあります。標準は同梱の LuaJIT ネイティブレーンで、 決まった形の重いピクセル処理はさらに CPU カーネルへ変換されます。Lua 5.2 互換の MoonSharp は フォールバック用の従来エンジンです。指定子を書かなくても、内容に応じて自動で最適なレーンが選ばれます。
エンジンの種類
| エンジン | 指定子 | 特徴 |
|---|---|---|
| ネイティブ(LuaJIT) | --!native(既定) | 別プロセスの LuaJIT で実行。ピクセル処理が大幅に高速 |
| MoonSharp | --!moonsharp | Lua 5.2 互換。フォールバック用の従来エンジン |
| CPU カーネル | --!cpu | 決まった形のピクセルループを並列実行へ変換。SIMD を実測で使い分け、MoonSharp と一致する結果 |
| GPU カーネル | --!gpu | ピクセルループを GPU シェーダーに変換。最速だが float32 精度(明示指定のみ) |
スライダー
obj.slider0 ~ obj.slider3 と文字列パラメータ
obj.text / obj.font / obj.dir / obj.file_* は、
MoonSharp とネイティブのどちらのエンジンでも同じ名前で参照できます。
CPU カーネルと GPU カーネルは数値計算に特化するため、文字列を扱いません。
指定子の書き方
エンジンの指定子は --! で始まる 1 行です。トークンの前後の空白以外に文字があってはいけません。
スクリプトのどの行に書いても構いません。
--!native
for y = 0, obj.h - 1 do
for x = 0, obj.w - 1 do
local r, g, b, a = obj.getpixel(x, y)
obj.setpixel(x, y, 255 - r, 255 - g, 255 - b, a)
end
end
自動振り分け
指定子が無いときは、スクリプトの内容から自動でエンジンが選ばれます。
- 後述のカーネル条件に当てはまるピクセルループは、まず GPU カーネルを試します。初回に実機の GPU で 処理し、CPU カーネルと出力が許容差に収まるか検証します。収まれば以後は GPU カーネル、収まらなければ CPU カーネルで実行します。CPU カーネルの結果は MoonSharp と一致します。
- それ以外のスクリプトは、同梱の LuaJIT ネイティブで実行します。結果は MoonSharp と一致します。
nativeフォルダが配備されていない環境と空のスクリプトは、MoonSharp で実行します。
明示した指定子は自動振り分けより優先されます。GPU カーネルは浮動小数点の 32 ビットで計算するため、
自動で選ぶときは初回の検証に通ったプログラムだけを対象とし、CPU カーネルと食い違うことはありません。
--!gpu を明示したときは検証に通れば GPU、通らなければ CPU カーネルで実行します。
カーネルに変換できる形
CPU / GPU カーネルは、次の決まった形のスクリプトだけを変換します。条件に合わないスクリプトは カーネルにならず、自動でほかのエンジンへフォールバックします(結果は変わりません)。
- 先頭に
local宣言を置いてもよい。 - 本体は二重の数値 for ループ 1 組だけ。外側と内側で
obj.hとobj.wをそれぞれ0から-1まで回す。上限にはlocal w = obj.wのような別名も使える。 - 内側ループの最初が
local r, g, b[, a] = obj.getpixel(x, y)、最後がobj.setpixel(x, y, ...)。 - その間は
local宣言と代入、およびif/elseif/elseによるlocal変数への条件代入のみ。
obj.getpixeldata() で得たハンドルの get / set を
local i = (y * data.width + x) * 4 の基底インデックスで使う形も同様にカーネル化されます。
ループの後の obj.putpixeldata(data) は置いても置かなくてもかまいません。
if 文の中で obj.setpixel や set を呼ぶ形はカーネル化されません。
カーネル内で使える要素は限られます。
| 分類 | 使えるもの |
|---|---|
| 演算子 | + - * / % ^、単項マイナス、条件式 cond and a or b |
| math 関数 | abs, floor, ceil, sqrt, sin, cos, tan, asin, acos, atan, atan2, exp, log, pow, fmod, min, max |
| math 定数 | math.pi, math.huge |
| 条件 | 比較(< > <= >= == ~=)、and / or / not、obj.check0 ~ obj.check3 |
| 参照できる値 | 座標 x / y、obj の各フィールド、scene.width/height/cx/cy、
time, frame, totalframe, framerate, timelineframe, timelinetime, layer, color |
CPU カーネルは、4 画素をまとめて計算する SIMD 経路を持ちます。SIMD が有利かどうかは
カーネルの内容と実行環境で変わるため、最初の数フレームでスカラ経路と SIMD 経路の両方を
実行して時間を計り、速かった経路をそのプログラムで使い続けます。どちらの経路でも出力は
同一です。
カーネルになるグレースケールの例
for y = 0, obj.h - 1 do
for x = 0, obj.w - 1 do
local r, g, b, a = obj.getpixel(x, y)
local gray = r * 0.299 + g * 0.587 + b * 0.114
obj.setpixel(x, y, gray, gray, gray, a)
end
end
フォールバックの動き
--!nativeを指定しても、プラグインの隣にnativeフォルダが配置されていない場合は MoonSharp で実行されます(ログに一度だけ警告が出ます)。期待した高速化が得られないときは、 ビルド・配置にnativeフォルダが含まれているか確認してください。--!gpuを指定しても、GPU カーネルを初期化できない場合は CPU カーネル、さらに MoonSharp の順で フォールバックします。--!cpuを指定してもカーネル条件に合わないスクリプトは、ほかのエンジンで実行されます。- MoonSharp では
tempbufferへの描画合成を GPU で実行し、GPU を利用できない環境では CPU の合成へ自動的に切り替わります。丸めの違いにより、合成の結果はエンジン間で ごく僅かに異なることがあります。
GPU カーネルは float32 精度で計算するため、MoonSharp / CPU カーネルとわずかに結果が異なることがあります。
厳密に一致させたい場合は CPU カーネルかネイティブを使ってください。