先ほど無事リリースされました。

ペイントツール FireAlpaca SE

これに合わせて幾つかフィルタをMFGストアに追加する作業をしています。 この作業が終われば現時点では10個くらいのフィルタになります。 まだまだ増やしていきたい。

自分がここ数年開発していたものの集大成です。なかなか感慨深いですね。反響があるといいなぁ。

私と@nattou_orgで分担して作っていた感じですが、ここでは自分が担当した部分の話を幾つか。

でかいキャンバスと大量のレイヤーでも爆速で動いて、最強のブラシと最強のフィルタを備えるペイントソフト

作ろうとしたのは、だいたいこの三つを備えたペイントソフト、となります。

プロフェッショナルユースとして、本格的なPCで使えばその性能を遺憾無く発揮する、というのは強く意識したところです。 普通の使用ではお目にかかれないような大きなデータでも十分に使い物になるような、 本格的なアプリケーションを作る事にはこだわりました。

また、その上で、ブラシとフィルタが最強なペイントソフトを作ろう、というのがコンセプトです。

まずはでかいキャンバスと大量のレイヤーでも爆速に動く、という話に関わることで自分のやった事をいくつかします。

そのあとで、フィルタの話をします。(ブラシの方は@nattou_orgの担当)

GUIスレッドを前提にしたノンブロッキングFutureとシステムのスレッドプールのサポート

サーバーサイドではマルチスレッド性能という点では定評のあるノンブロッキングFutureですが、 性質上スレッドプールとセットになってしまう事が多く、 それはGUIスレッドやイベントループという構造がすでにあるGUIアプリケーションとは都合が悪いものでもあります。

GUIフレームワークは大体そのGUIスレッドとちゃんと動くことが保証されているスレッドプールが提供されていて、 それ以外のスレッドプールと混ぜて使うと最高のパフォーマンスは発揮出来ない事も多い。

そこでGUIアプリケーション用にGUIスレッドを意識したノンブロッキングFutureを実装して使っています。 これはネイティブのスレッドプールを使う事を想定していて、 またExecutorとしてGUIスレッドに返すようなケースも最初から想定して作られています。

GUIスレッドのキューをどう消費するかはシチュエーションごとにさまざまな特殊な制約があるのが本格的なグラフィックスエンジンの常なので、 それらを隠さない代わりに柔軟に対応出来るようになっています。

ノンブロッキングFutureはコードのアーキテクチャに与える影響も大きく、 大きな変更を要求する決断ですが、 マルチスレッド性能に関してはスレッドとロックでは決して得られないレベルのスケーラビリティを達成しつつ、スレッドが少ない環境でも少ないなりに動きます。

GUIアプリケーションに特化したノンブロッキングFutureと、それに伴う数々のユーティリティ的なクラス群は、かなりオリジナリティのあるチャレンジだったのではないか、と思っています。

耐障害性を兼ね備えた大容量対応差分保存ファイルフォーマット(mdz)

現代的なappend onlyのKVSをベースにしたアーキテクチャでありながら単一アプリケーション用にと設計されたファイルフォーマットです。 並列アクセスや差分保存を前提に作られていて、 プロフェッショナルが仕事で扱うような大きなキャンバスのデータでも高速で読み書き出来て、 それでいてファイルサイズも膨れ上がらず、 保存の途中のクラッシュなどでもファイルが壊れないような耐障害性も兼ね備えています。

ノンブロッキングFutureと合わせて、コアの数をフルに活かしたスケーラブルな読み書きを実現します。

本格的なアプリケーションはだいたいKVSを作り直す、という例にもれず、FireAlpaca SE 3.0でも独自のKVSから作っています。 一つのアプリケーションからしかアクセスされなくて、けれどたくさんのスレッドからアクセスされる、そして一つ一つのファイルサイズが重要、 といったあたりは既存のKVSとは違う要求ですね。

ロックフリー並列アロケータ

同一サイズのメモリを大量に確保するというアプリケーションの特性に合わせて開発した、 アプリケーションのメモリ使用パターンに最適化したロックフリーの並列アロケータです。 アプリ固有のアロケーションパターンに最適化して設計したため、汎用のアロケータでは得られないパフォーマンスと省メモリを両立しています。

一言でいえば、デスクトップ向けアプリケーションに期待される振る舞いを前提に、自身のアロケーションパターンに特化して作り直したmimallocのようなもの、になっています。

システムに対して単一のアプリケーションとしてお行儀よく振る舞うために、 不要な時に余計な予約を行ったりはしません。 けれど、大きなキャンバスを開く時などのメモリを大量に要求するシチュエーションでは素早く伸長します。 また、多くのコアがある場合での強い負荷の元でのパフォーマンスにも十分に最適化されていて、 TLSで済む範囲の時にはなるべくその範囲で動き競合を最小限に抑えつつ、 競合する部分もロックフリーに実装されていて、並列性能も十分に検証、最適化されています。

最強のフィルタ処理系、MFG

最強のフィルタを備えたペイントソフトを作る、ということで開発されたのがMFGという言語です。 FireAlpaca SE 3.0では、このMFGで書かれたフィルタ(をmarという形式にパックしたもの)を組み込むことが出来ます。

MFGで開発したフィルタは現時点でもMFGストアで公開されていて、今後もどんどん追加してきます。 また、ユーザーもMFG Studioを用いて開発を行う事ができます。

このMFGに関してはかなりの長期間開発してきたもので、語りたい事も無限にありますが、今回は高品質なフィルタをどんどん増やしていけるためにどういうものになっているのか? という視点からいくつか書いていきたいと思います。

ユーザーやサードパーティーの作ったフィルタを組み込むことが出来る、という、一見簡単そうな話が、なぜ実現が困難で、それをどうやって解決したのか、 実際に実用できるようなものに仕上げるにはどれだけの事をやらないといけなかったのか、 という事を書いていきたい。

フィルタはいつも強いパフォーマンス要求にさらされる

フィルタを独自な言語で書けるようにする、というのは、 一見すると大したことの無い話のように見える。 適当にluaとか持ってきてくっ付ければいいのでは?と思うかもしれない。

例えばFireAlpacaには2の頃から、ブラシスクリプトというluaで書いたブラシを使う機能がある。

けれどフィルタは、これではうまく行かない。 何故なら、フィルタは全ピクセルに対して実行しなくてはいけない処理だからです。 ピクセル数は縦と横で2乗で効くので、1000x1000と10000x10000では全く違うパフォーマンス要求となります。

CPUの速度の向上が、ピクセル数の上昇に全く追いついていない、 というのは、良く知られた話です。

通常よくあるような、柔軟で簡単だが少し遅いスクリプトでユーザーが自由にカスタマイズ出来る、という選択肢が、フィルタに関してはとる事が出来ません。

MFGの開発でも、初期の頃から実用的なパフォーマンスを達成するために多くの試行錯誤を行ってきました。

ネイティブdllのプラグインの多くの欠点

厳しいパフォーマンス要求の結果、これまでは拡張可能なフィルタというとネイティブのdllによるプラグイン、という形を取る事が多かった。 ですが、これには多くの欠点があります。 実際多くの欠点があるからこそ、フィルタ以外ではだいぶ使われなくなった方式でもあります。

まず開発環境を整えるのが困難です。dllを作成するためにVisualStudioなどの開発環境を整備して、その上でプラグイン用に提供されているSDKを設定しなくてはいけません。 プラグイン開発はそれほど開発者も多くないためドキュメントも概して少なく、トラブルが起きた時の解決にもかなりの技術力を要求されます。

また、dllのプラグインの開発というのは、それ自体が割と面倒な開発に部類されます。うまくロードされない時にその原因を調べるのもそれなりに手間ですし、 デバッガで止めるのがうまく行かないなどのトラブルも良くあります。 ましてやユーザーの手元でしか起こらないバグを追うのは、かなりの困難を伴います。

そしてネイティブdllによるプラグインは通常C++となりますが、C++はお世辞にも初心者に優しい言語ではありません。 しかもフィルタは先ほども述べた通り、強いパフォーマンス要求にさらされるものです。 単にC++を使うだけでは不十分で、かなり頑張ったC++を書かなくてはいけません。

ネイティブのdllはセキュリティ的にも不安があります。 仕組みとしてはなんでも出来るものであるため、適切なサンドボックスやverifyを実装するのはアプリの責任であり、 これは不可能では無いにしても困難な仕事で、アプリがそれを適切に行っていると容易に期待できる類の事でもありません。 実際かつてはdllのプラグインをサポートしていたが、セキュリティ的に問題が多いのでのちに廃止した、というアプリは多くあり、むしろそちらが主流とすら言えます。

そして最後に、ネイティブのdllはポータブルではありません。Windows用に作られたバイナリはMacでは動きません。 Windowsでは使えるプラグインがMacでは使えない、といった事は普通の話です。

MFGはGPU上で高速に動く

以上の問題にMFGではどのように対応したのか?という事を以下に書いていきます。

MFGはGPU上で動きます。しかも、単にGPU上で動くというだけではなく、普通に書いたら自然とGPU的に早いコードになるように言語が設計されています。 通常のシェーダーはCPUのように書けるけれどCPUのように書いてはいけない、という落とし穴があります。MFGではそんな落とし穴はありません。

MFGでは一つのカーネルが長くなりすぎるようなコードが書きにくくなっています。 また、カーネルの分割が容易でSPUを使い切りやすい構造になりやすくなっています。

自然に書くと自然にフィルタに要求されるようなパフォーマンスを満たすようなコードになりやすいように作られているのがMFGです。

これは自分が過去にCUDAやOpenCLなどでパフォーマンスを出すのに苦労したり、Halideなどで複雑になってくるとうまくスケジューリングをするのが凄く大変だった、という体験から重視した項目です。

MFGはポータブルである

これは言うまでも無いことに感じられますが、ネイティブのプラグインという比較からすると重要です。

現時点でもHLSL(つまりDirectX)とMetalで動いています。 FireAlpaca SE 3.0はiPad版は無いですが、iOSでも動いています。(iPadの実機でかなり早く動くので感動があるのですが…)

知っている人ならわかると思いますがHLSLは相当に他と違う環境なので、これとMetalが動けば大体の環境には移植可能と言えるでしょう。必要になったらOpenGL系への移植はやりたいと思っています。

MFGは安全にロードできる

MFGはIRを解析しやすいように元から設計された言語で、シェーダー用語で言うところのホストに相当するものなどはIRを解析して自動的に動きます。 MFG自身がホストを動かす言語では無いので、ホストにロードされて動くほかの言語に比べると安全性が最初から高いようなアーキテクチャとなっています。

最初からIRが解析しやすいように、というのは言語設計で気をつけた所でもあります。

またバッファのアクセスなども非常に制限された形でアクセスされるように設計されているため、境界値チェックなども容易で、またそれを行わなくてはいけない場所も局所的になっています。

国際化やテクスチャなども含める事が出来るMAR

MFGはMARという形式にパッケージングして配布します。このMARには文字列リソースやテクスチャなども含める事が出来ます。

国際化などは入門時にはあまり気にする必要は無いものですが、 MFGは実際に我々がフィルタを開発して提供する事が出来るように十分な機能が含まれています。 これには国際化などの仕組みも含まれます。

こうしたパッケージの仕組みも考えて実装する必要がありました。

また、それらを作成するためにMFGStudioという開発環境を作る必要もありました。

開発環境のMFGStudioやprism.jsのハイライトを作った

開発環境をどうするのか?というのもかなり悩んだ所なのだけれど、やはり簡単にセットアップ出来てすぐに始められる環境を提供するなら、 自分で開発環境を作るしか無いな、という結論になり、MFGStudioを作りました。

また、ドキュメント用のためにprsim.jsのMFGの文法対応を作り、それを使ってドキュメントの視ンタックスハイライトを行っています。

言語以外にもいろいろ作らないといけなかったのでいろいろ作りました。

IDEを作ったりシンタックスハイライト作ったりドキュメント書いたり言語処理系作ったり国際化の仕組みやパッケージング作ったりいろいろいやりました。 一人で全部やるってすごくね?

MFGの言語設計は自分のプログラム言語に対する思いの全てを詰め込んだ

プログラム言語としてのMFGに関しては、これまでも多く語ってきた事でもあるのでここには書ききれませんが、 フィルタを開発する側の視点で重要な点について少し触れておきたいと思います。

MFGは、モダンな多くの言語と同様に副作用がなるべく無いように作られているし、 ループに関しても限定的な形で終了が自明な形式を多く揃えています。 型もあまり暗黙でキャストされたりせず、変なバグが入りにくくなっています。

パイプラインや強力な式のおかげで簡潔に書けて、書いていて楽しく、 ややこしいアルゴリズムを書いていても変なバグが入りにくく、 フィルタを書こう!という気分にさせてくれます。

実際たくさん書いていますが、書いていて本当に楽しい言語に出来たな、と思っています。

自分個人は、フィルタは現時点で100個くらいは書いていると思う

当然ですが現時点では、最もMFGのコードを書いているのは私です。

MFGストアには現時点では10個のフィルタが登録されています。 また、MFGStudioにはサンプル集として26個のフィルタが入っています。 これらは全て私が書きました。

これ以外にも、これらに含まれないボツのものや、 ユーザーに使ってもらう目的では無い個人的興味のフィルタ(ベジエを書いてみる、とか、バケツの線の漏れを塞ぐアルゴリズムはGPUで実現可能か?とか)や、 MFGのフィルタの開発に便利なツール的なもの(特定のグラデーションやアルファ値のパターンで埋める事でデバッグを用意にするインプットを用意するためのもの)、 そのうち含めようと思っているがまだそこまではいってないものなど、 多くのフィルタを書いてきました。

全部でいくつあるかは数えてはいませんが、50個以上は間違いなくあり、多分100個くらいは書いたんじゃないかな?と思います。

これらのうち初期のものはMFGStudioを作る前に書いたものですが、最近のものは全てMFGStudioで開発しています。 MFGStudioを一番使っているのも現時点では自分でしょう。 MFGStudioもMFGも、まだまだ不足はありますが、それでも現時点で十分にフィルタを開発していけるだけの言語機能、IDEの完成度を提供出来ているのは、 自分が身をもって示せていると思っています。

やはり自分たちがフィルタを提供するのに使える仕組みじゃなければほかの誰も使ってくれない、と思うので、 自分たちが新たなフィルタをどんどん提供していくのにこれを使っていきたいと思えるものを作ろうとは最初から思っていたところで、 それは実現出来ているように思う。

そして今後も自分が積極的に使ってフィルタを作っていくつもりでいます。まずは自分が使う。しかもめちゃたくさん使う。

まとめ: FireAlpaca SE 3.0はめちゃ頑張って作ったので反響あると嬉しいな

という事でFireAlpaca SE 3.0、めちゃ頑張って作りました。 「でかいキャンバスと大量のレイヤーでも爆速で動いて、最強のブラシと最強のフィルタを備えるペイントソフト」は作れたと思っています。

これが求められている事なのか?は今回のリリースの反響で明らかになる事かな、と思っています。 どういう結果になるのか、凄く楽しみです。 反響あるといいなぁ。

という事でめちゃ頑張って作りました。 爆速最強ペイントアプリです。フィルタもどんどん追加してく予定です。 ちゃんと違いがわかるアーティストの手に届けば、ちゃんと伝わるものが出来たと自負しています。