7月11日、magik.netが「FreeCAD in your browser」と題した記事を公開した。オープンソースの3D CADソフトウェア「FreeCAD」——機械設計から建築まで幅広く使われるデスクトップCADツール——をWebAssemblyでブラウザ上で動作させるまでの技術的な過程を詳細に記録した内容だ。
驚くべきはその規模と速度だ。FreeCADのコードベースはC++ 約150万行、Python 約70万行で、過去にブラウザ移植が行われたLibreCADやOpenSCADと比べ約1桁大きい。それがわずか約4日間で動くようになった。実装を担ったのはmagik.netが開発するAIエージェント「Fable」で、4日間にわたる48本のプロンプト全文と全サブエージェントのセッションログが公開されている。単に動いたという話ではなく、どうやって動かしたかが丸ごと追える点が技術者の間で注目を集めている。なお、このプロジェクトはHacker Newsのスレッドを起点に始まったものだ。
なぜFirefox・Safariでは動かないのか——JSPIが鍵
動作確認にはChrome/Edge 137以上が必要で、Firefox・Safariはまだ非対応だ(初回ロードはBrotli圧縮で約96MB、以降はキャッシュされる)。非対応の理由はWebAssembly JSPI(JavaScript Promise Integration)の実装状況にある。JSPIとは、WebAssemblyの実行スタック全体を一時停止(サスペンド)し、JavaScriptのPromiseが解決されたあとに再開する仕組みだ(W3C提案)。現時点ではChrome/Edgeのみが対応している。
なぜJSPIが必須なのか。FreeCADのコードベースには、モーダルダイアログを同期的にブロックするQDialog::exec()の呼び出しがC++内に185箇所、Pythonコード内にさらに156箇所存在する。ブラウザのシングルスレッドモデルでは同期ブロックが許されないが、341箇所をすべてコールバック形式に書き換えるのは非現実的だ。JSPIを使えばwasmスタックごとサスペンドできるため、既存コードをほぼ変更せずに済む。
このJSPIを利用するには-feature-wasm-jspiフラグ付きでQtをソースからビルドする必要があり、ツールチェーン自体の構築から始まった。最終的なビルド環境はQt 6.11.1(wasm+JSPI)、Emscripten 4.0.12、OpenCASCADE・CPython・ICU・Boostなどを手動ビルドした静的ライブラリ群で構成されている。
「本物のOpenGL」をWebGL2で動かす——900行のエミュレータ
FreeCADの3DビューはCoin3Dというシーングラフライブラリを経由し、1990年代の固定関数パイプラインOpenGL(glBegin/glVertex、行列スタック、クライアント頂点配列など)を大量に呼び出す。WebGL2はそれらを一切サポートしない。
対応策として実装されたのが約900行の固定関数エミュレータ(WasmGLFixedFunc)だ。Coinが実際に使うOpenGLのサブセットをGLES 3.0上でエミュレートし、CPU側で行列スタックを管理、GLSLプログラムで2光源のピクセルシェーディングを再現する。
デバッグ過程の記述が具体的で読み応えがある。「ソリッドが全部消える」バグでは、glGetDoublev(GL_DEPTH_CLEAR_VALUE)をスタブで0返しにしていたため深度バッファが最手前でクリアされ全ポリゴンが落とされていた。「頂点配列の高速パスで画面が点の集まりになる」バグでは、エミュレータ自身のスクラッチVBOが描画後も結合したままで誤ったバッファから頂点を読んでいた。この修正により描画が約1.3fpsのimmediate-modeから快適な操作感へと改善した。
前例のない挑戦——PySide6をwasm上で動かす
記事中で「誰もやったことがない」と明記されているのがPySide6のwasm動作だ。FreeCADのPythonワークベンチはPySide6(QtのPythonバインディング)を使ってUIを構築するが、これまでwasm上での動作実績がなかった。shiboken6とPySide6を静的リンク・dlopen不使用の形でビルドし、wasm版CPythonと繋ぎ込むことで、PythonコードからQtウィジェットを操作できるようになった。QtCore、QtGui、QtWidgets、シグナル/スロット——これらがすべてブラウザのタブ内のPythonから動作する。
wasmの例外エンコーディング問題と46%のサイズ削減
ビルド中、V8が「例外エンコーディングが混在するwasmモジュールを拒否する」という問題も発生した。wasmの例外には旧来のtry/catch命令と新しいtry_table/exnrefの2種類があり、C++全体を新エンコーディングでビルドしても一部のsetjmpベースのオブジェクトが旧命令を出力し続ける。解決策はリンク後にBinaryenのwasm-opt --translate-to-exnrefパスを通してエンコーディングを統一することだった。この対応で旧来のasyncifyビルドと比べサイズが46%削減、速度も向上した。
FEMワークベンチ:VTK+SMESHもwasm化
有限要素法(FEM)解析ワークベンチの対応が最も工数を要した。FEMはメッシャーのSMESHと可視化ライブラリのVTKに依存しており、両者ともwasm向けのビルド実績がなかった。VTK 9.3.1のデータモデルサブセット(レンダリングなし、逐次処理)をwasm向けにビルドしSMESHとリンク。ABI不一致を修正した後、FEMサンプルは43オブジェクトを正常にリストアし、メッシュジオメトリ・解析結果パイプライン・デフレクションプロットがすべて表示されるようになった。ブラウザ上でフル機能のFEM解析が動く環境はこれが初とみられる。
最終成果物の構成
最終的な成果物は196MBの単一wasmモジュールで、OpenCASCADE(B-rep形状カーネル)、Coin3D + Quarter、CPython 3.14(フル組み込み)、PySide6 / shiboken6、VTK 9.3 + Salome SMESH、17以上のワークベンチ(Part、Sketcher、PartDesign、FEM、CAM、BIM、Assembly等)、Qt 6.11が含まれる。ブラウザ上でFreeCADを直接試すこともできる。
AIエージェントが4日間で行ったことの全ログが公開されているという点は、「大規模レガシーC++コードのwasm移植」という難題に取り組む際の実践的な参考資料になりうる。
詳細はFreeCAD in your browserを参照していただきたい。