Slide 1

Slide 1 text

Kouji Matsui (https://www.kekyo.net/) maplibre-gl-layers 地図に移動体 たくさん表示したい Kouji Matsui Center CLR No.11 2026/2/28

Slide 2

Slide 2 text

Kouji Matsui (https://www.kekyo.net/) Kouji Matsui ● kekyo, けきょ , kozy_kekyo というクレジットを使うことがあります。 ● 愛知県の付近のモグリで引き籠モラー。 ● 自転車乗りです(最近忙しすぎて乗れてない) ● Center CLR 主催しています。 ● Ubuntu/Debian 使い。バックエンドシステム・言語処理系とそのツール チェイン・ライブラリインターフェイス設計、などが主戦場 / 興味の中心。 ● 赤い色は、某 WP 1520 由来。 今ではマイカラー。燃える赤 🔥

Slide 3

Slide 3 text

Kouji Matsui (https://www.kekyo.net/) Agenda ● なんで作ったか ● 特徴 ● デモ ● 内部構造

Slide 4

Slide 4 text

Kouji Matsui (https://www.kekyo.net/) MapLibre って ? ● MapLibre (https://maplibre.org/): 地図表示ライブラリ ● leaflet (2012) --> MapBox --> MapLibre GL (now!) ● leaflet は有名なライブラリだけど、もう更新されてない (?) ● MapBox は、 GIS に関するサービスを提供する企業。 leaflet の中の人は MapBox に Join 。その後、そこから地図表示ライブラリを OSS として分 離したのが MapLibre GL (らしい)。

Slide 5

Slide 5 text

Kouji Matsui (https://www.kekyo.net/) MapLibre GL JS って ? ● ブラウザ環境で WebGL を使用して地図を描画できる MapLibre GL シリーズの実装の一つ。 ● WebGL は、ブラウザ JavaScript 環境で 2D/3D 描画が可能なウェブ標準 API ● 実際、 WebGL ってどうなの? – 基本構造は OpenGL に似ている(らしい) – WebGL1 と WebGL2 がある。 – 一般ブラウザで普通に使えます (FireFox, Chrome, Safari( 未確認 )) – MapLibre GL JS は WebGL1 を 使って描画しているようだ。 – もう GMap は一般化したのだ。

Slide 6

Slide 6 text

Kouji Matsui (https://www.kekyo.net/) MapLibre GL JS って ? ● leaflet とどう違うのか? – leaflet は 2D 表現(地図鉛直見下ろし)、かつタイル画像表示。 – MapLibre GL JS では、 2.5D 表現が標準。 3D 地図データ供給によって 完全 3D 表現も可能(らしい。試してない) – MapLibre GL JS では、タイル画像でもベクトルデータでも OK ! ベクトルデータは OSM-PBF 形式(まだ本格的に普及してない) – 完全 3D が実現すれば、それはつまり GEarth があなたのアプリケー ションで実現出来るという話なので、夢がひろがりんぐ。 (しかし、本日の話とは関係ありません)

Slide 7

Slide 7 text

Kouji Matsui (https://www.kekyo.net/) MapLibre GL JS って ? ● 2.5D って? (以下は同じ場所。おわかりいただけたであろうか) ● 視点(カメラ)のピッチコントロールが可能ということ。

Slide 8

Slide 8 text

Kouji Matsui (https://www.kekyo.net/) MapLibre GL JS って ? ● 地図の表示 MapLibre プロジェクトの デモ用 2.5D ベクトルデータ

Slide 9

Slide 9 text

Kouji Matsui (https://www.kekyo.net/) MapLibre GL JS って ? ● OpenStreetMap OpenStreetMap が公式で タイルサーバーを提供

Slide 10

Slide 10 text

Kouji Matsui (https://www.kekyo.net/) MapLibre GL JS って ? ● つまり、 MapLibre GL JS は「地図の表示」のみ行うライブラリ ● ここに地図データ(タイル画像またはベクトルデータ)を供給して地図の 表示が実現します。 MapLibre GL JS ブラウザ (WebGL) WebGL API 地図データプロバイダー ( タイル画像 / ベクトルデータ ) データソースアクセス

Slide 11

Slide 11 text

Kouji Matsui (https://www.kekyo.net/) MapLibre GL JS って ? ● 地図上にマーカーを表示する FeatureCollection (geojson)

Slide 12

Slide 12 text

Kouji Matsui (https://www.kekyo.net/) MapLibre GL JS って ? ● “FeatureCollection” にマーカーを格納して設定すると表示される。 geojson 形式 : https://geojson.io/ ● マーカーは複数可。座標だけでなく、いくつかの属性がある : – 座標点・線分(ラインストリング)・ポリゴン – イメージ・サイズ・回転・ラベルテキスト : これらは properties とし て定義して、スタイル式を指定して反映させる – やや複雑だが、機能的には問題ない。 – イミュータブルインターフェイスなのは良い。 ● FeatureCollection の内容を更新すると、そのレイヤーの全体再描画が発生 する

Slide 13

Slide 13 text

Kouji Matsui (https://www.kekyo.net/) MapLibre GL JS って ? ● FeatureCollection が大量のマーカーを描画しているときに、その一部を変 更すると、画面がチラチラしてしまう ... (少量なら目立たない) ● (恐らく) FeatureCollection の内容を再度 WebGL に転送するためのデー タを再生成していて、これがとても重く、再描画(正確には再描画に至るま での時間)が遅いので、見えてしまう。 ● 他に、 Marker オブジェクトを使う方法もあるが、これは HTMLElement 化 されるので、たくさん表示するとしぬ。 ● 動くアイコンを置きたいんだけど、もしかしてこれ詰んだ?

Slide 14

Slide 14 text

Kouji Matsui (https://www.kekyo.net/) Agenda ● なんで作ったか ● 特徴 ● デモ ● 内部構造

Slide 15

Slide 15 text

Kouji Matsui (https://www.kekyo.net/) maplibre-gl-layers ● FeatureCollection や Marker を使うこと無く、 大量の(アイコン)画像を表示して、 しかも瞬時に座標位置を移動できます。 ● MapLibre GL JS のレイヤー機能を使い、 レイヤープラグインとして実装。 ● WebGL を直接使って描画します。 ● MapLibre GL JS の初期化から、実際に 何か表示するまでの最短ステップが 簡単に実装できる。

Slide 16

Slide 16 text

Kouji Matsui (https://www.kekyo.net/) maplibre-gl-layers ● 基準座標点 ● 画像の不透明度・回転方向・自動回転 ● 引き出し線描画・疑似 LOD ● 基準位置に対するオフセット・基準位置のリレーション ● 上記パラメータ群に対する自動補間動作(イージングアニメーション) ● 表面描画モードとビルボードモード ● スケール・アンカーオフセット ● ラベル描画(テクスチャベーク方式) ● WASM 計算オフロード 赤字は追加機能

Slide 17

Slide 17 text

Kouji Matsui (https://www.kekyo.net/) maplibre-gl-layers ● 最小コード例 (MapLibre の初期化 ) 地図表示領域が潰れて しまわないようにする MapLibre の生成と配置

Slide 18

Slide 18 text

Kouji Matsui (https://www.kekyo.net/) maplibre-gl-layers ● 最小コード例 (maplibre-gl-layers を初期化してスプライトを配置する ) 初期化して MapLibre に追加 アイコン画像を登録 スプライトを追加 (これで表示される)

Slide 19

Slide 19 text

Kouji Matsui (https://www.kekyo.net/) maplibre-gl-layers ● https://github.com/kekyo/maplibre-gl-layers-demo

Slide 20

Slide 20 text

Kouji Matsui (https://www.kekyo.net/) Agenda ● なんで作ったか ● 特徴 ● デモ ● 内部構造

Slide 21

Slide 21 text

Kouji Matsui (https://www.kekyo.net/) デモ ● デモページ : https://kekyo.github.io/maplibre-gl-layers/ ● ランダムに配置した矢印がランダムに動き続ける。ほぼすべてのパラメータを実験できる。

Slide 22

Slide 22 text

Kouji Matsui (https://www.kekyo.net/) デモ ● WASM 制御 ● スケーリング ● マウスイベント トラッキング ● 地図選択 Light/Dark

Slide 23

Slide 23 text

Kouji Matsui (https://www.kekyo.net/) デモ ● レイヤー有効化 補間有効化 ● スプライト数 ● スプライトモード ボーダー表示 ● 移動補間モード

Slide 24

Slide 24 text

Kouji Matsui (https://www.kekyo.net/) デモ ● 自動回転 ● 不透明度 疑似 LOD ● 追加イメージ ラベル 引き出し線

Slide 25

Slide 25 text

Kouji Matsui (https://www.kekyo.net/) デモ ● 動作確認は、一般的な PC (Ubuntu 24.04, amd64) で FireFox と Chrome (Chromium) で行いました。 ● 少しだけ NanoPi R5C (Debian, arm64) で動かしたけど、一応動作。 1000 スプライトはちょっと重い ● なかなか恐ろしいことに、デスクトップ PC (Core i9-9980XE/RTX4060Ti) よ りも、この ThinkPad X1 Carbon Gen11 (Core i7-1370P) のほうが速いです (後述)

Slide 26

Slide 26 text

Kouji Matsui (https://www.kekyo.net/) Agenda ● なんで作ったか ● 特徴 ● デモ ● 内部構造

Slide 27

Slide 27 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 ● 全体的な構造は以下の通り : レイヤー実装群 ( 他のレイヤーと同時に使用できる ) MapLibre GL JS maplibre-gl-layers render() ブラウザ (WebGL) WebGL API maplibre-gl-layers maplibre-gl-layers 地図プロバイダー ( タイル画像 / ベクトルデータ ) データソースアクセス GPU 描画

Slide 28

Slide 28 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 ● 全体的な構造は以下の通り : 計算処理・切り替え可能 MapLibre GL JS maplibre-gl-layers render() ブラウザ (WebGL) WebGL API maplibre-gl-layers JavaScript 計算 WASM 計算 WASM SIMD 計算 WASM SIMD 計算 ( マルチスレッド ) スプライト画像アセット registerImage() GPU 描画

Slide 29

Slide 29 text

Kouji Matsui (https://www.kekyo.net/) WebGL ● レイヤーライブラリは CustomLayerInterface を実装する必要がある。 ● MapLibre GL JS は WebGL の描画が必要になると、 CustomLayerInterface.render 関数を呼び出す。 レイヤーライブラリはそのタイミングで WebGL を操って描画する :

Slide 30

Slide 30 text

Kouji Matsui (https://www.kekyo.net/) WebGL ● WebGLRenderingContext / WebGL2RenderingContext: OpenGL ES 2.0/3.0 をベースにした WebGL インターフェイス ● CustomRenderMethodInput: 地図描画に必要な様々な情報にアクセスできる MapLibre のインターフェイ ス。例えば、カメラの位置・角度をカプセル化した投影パラメータなど。 ● つまり、 options の情報を見ながら、 gl で描画する。

Slide 31

Slide 31 text

Kouji Matsui (https://www.kekyo.net/) WebGL ● WebGL は「シェーダープログラム (GLSL) 」を書いて、そこにデータ群を 渡して GPU でプログラマブルに描画する。 ● ウェブ技術界隈で GPU だのシェーダープログラムだの言ってもかなり浮世 離れな感じだけど、実際、 JavaScript 文字列でシェーダーコードを投入し て、 JIT コンパイルされて GPU で描画される glContext: WebGL オブジェクト ( 注 : JavaScript です )

Slide 32

Slide 32 text

Kouji Matsui (https://www.kekyo.net/) WebGL ● 最近のブラウザゲームは WebGL を使うのが当たり前(ということは知って いたが、コードの実感としては無かった ... ) https://deepnight.net/games/nuclear-blaze/

Slide 33

Slide 33 text

Kouji Matsui (https://www.kekyo.net/) WebGL ● WebGL はかなり生々しい感じだけど、 WebGL 1/2 ともに既に主要ブラウ ザで十分サポートされているので、動作環境について心配は無用 ( Firefox,Chrome,Safari など)。 ● WebGL2 についてもある程度サポートされているようだが : – WebGL1 とシェーダー言語が違っているので、変更が必要。 Polyfill の ようなライブラリもあるようだが… ? – WebGL2 を少し試していた時に、ブラウザインスタンスのプロセスが刺 さって死んだことがあった。 WebGL1 ではそのような経験は無かっ た。

Slide 34

Slide 34 text

Kouji Matsui (https://www.kekyo.net/) WASM ● WASM (WebAssembly) は、ブラウザ上で隔離された安全な環境で動作でき る中間言語を用いたプログラム。 雑に言えば .NET CLR や JVM のようなもの。 ● WASM バイナリをブラウザ上で読み込ませて、実行できる。 ● JIT コンパイルされるので、一般的に JavaScript よりも速い。 ● C/C++, Rust などが使える。 LLVM-IR から変換出来るので、他の処理系も対 応しやすいと思われる。 ● SIMD 命令にも対応している。 ● マルチスレッドも出来る(制約がある)。

Slide 35

Slide 35 text

Kouji Matsui (https://www.kekyo.net/) WASM ● 隔離された環境とのラウンドトリップはコストが大きいので、密な呼び出し は高コスト。 つまり、如何に呼び出し頻度を抑えて WASM で計算させられるかが鍵。 ● maplibre-gl-layers では、入力パラメータを全てバッファに集めてから WASM の関数を呼び出し、そこで全部計算してから出力バッファに書き込 み、その内容を JS 側で取り出して WebGL を呼び出す、というような構造 にすることを目指した。 ● SIMD 命令は自動ベクトル化はあまり効果がないので、明示的に指定(後 述)

Slide 36

Slide 36 text

Kouji Matsui (https://www.kekyo.net/) WASM ● WebGL のシェーダープログラムでも計算できる。つまり GPU で計算した ほうが効率が良いしエコ(低消費電力)を狙えるのでは? ● 残念ながら WebGL の最大の制約、浮動小数点は 32 ビットまでしか対応し ていない問題。 WebGL2 でも不可。 ● 32 ビットで世界測地系の lng/lat 座標を扱うと、精度が 1 桁メートルぐら いになる(ユニバーサルメルカトル図法なので場所による)ので、拡大した 地図上でアニメーションさせると「ガタガタ」とした動きになってしまう… ● もう 1 桁良ければ許容できたかもしれないので非常に惜しい ... 地球が縮ま る事を望む ...

Slide 37

Slide 37 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 ● 実装中の問題点の推移 (1) – レイヤープラグインを 3D モードで実装したら、良くクラッシュする ( WebGL2 ) – 実際のところ、 3D オブジェクトを表示する予定はないので、 2.5D モードで実装することにした。これにより WebGL1 をターゲットとす ることになった。

Slide 38

Slide 38 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 ● 実装中の問題点の推移 (2) – シェーダーで簡易的な補間アニメーションを実装したら、動きがガタガ タ ... – ここで過去に GIS 案件やった時に 32 ビット浮動小数点では精度が足り なかったことに気が付き、シェーダーで補間計算を実装することを諦め る。 – しかし、 JavaScript で計算させると、スプライトを大量に表示させた 時にアレなので、どうにか出来ないか考えた末に、 WASM の事を思い 出す。

Slide 39

Slide 39 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – C/C++ なら分かるので、 Emscripten SDK ( WASM コードを生成でき る clang 処理系)を使用して WASM バイナリを実装する事にする。 – 世の中 Rust が流行っているのでここで取り組むかちょっと迷ったが、 まだ完成まで先が長いのと、あまり悠長に実装していられないのと、ど うせ計算処理だけなので、という判断で使用。 – 言語スイッチングコストが楽だったので、結果的に良かった。

Slide 40

Slide 40 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 ● 実装中の問題点の推移 (3) – ビルボードモード : どの方向から見ても、スプライトが常に 「カメラに正対」する。 HUD アイコンなど。 – 人間がスプライトを視認するには非常に良い。 特に 2.5D 表現では、ピッチが寝ていると 遠方に描画されたスプライトが視認しづらい。 ビルボードモード 向きが変わっても見え方が同じ サーフェイスモードで 極端にピッチが寝ている遠方

Slide 41

Slide 41 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – 通常の、地図上にステッカーを貼ったかのように描画するモードを 「サーフェイスモード」と呼んで区別する。 – サーフェイスモードは地図平面に描画するので、 3D 計算としては自 然。一方、ビルボードモードは、カメラ - 地図平面の投影行列 (projection matrix) を無視して計算する。 – ここまでの話なら、投影行列の有無だけなのだが。

Slide 42

Slide 42 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – スプライトを回転させる機能には、回転軸が 2 系統ある : ● ユーザーが指定する回転角 ● 自動回転機能による回転角(座標移動のベクトルから回転角を計算 する) – サーフェイスモード : 指定回転角+自動回転角が、北方向( Y 軸 + )を 基準に時計回り – ビルボードモード : 指定回転角+自動回転角が、ビューポート上方 (??) を基準に時計回り

Slide 43

Slide 43 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – ビューポート上方とは、地図上の北方向とは無関係なので、一体これを どう扱えばいいのか頭がウニになってしまった ... – 一旦ワールド座標系でのカメラからの視錐台の底に投影させ、そこで回 転させた後、それを元に計算を行ってシェーダーに流すとか ? (なんか、メチャクチャ複雑な事を考えていた) ChatGPT に書かせてみたけど不正確なので 脳内で置き換えて下さい ...

Slide 44

Slide 44 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – 結局、ビルボードモードで回転角を与えたり、自動回転を行うと、意図 しない方向を向くかも知れないという、仕様上の制約にした。 (正確には、北磁方向を向いている場合は正しくなる) – ビルボードモードで描画したいスプライトは、要するに HUD のアイコ ンのように、視覚的な目印に使う事が目的であろうから、そもそもそれ を回転させたいことはあまりないだろう、という目算もある。 – 割と最近、ビルボードに真の意味付けを行うことが出来た(と感じてい る)ので、何か改良を入れるかもしれない。

Slide 45

Slide 45 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 ● 実装中の問題点の推移 (4) – ラベル表示のテキストをどうやって描画するか? – HTMLElement を(座標だけどうにか計算して)配置すれば、後は HTML+CSS の世界で自由にやれる。しかし、 HTMLElement にすると スプライトが増えた時にしぬ。 – WebGL でのテキスト描画を調べたところ「そんなものはない」

Slide 46

Slide 46 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – 現実的にはどうやって解決しているか? ● MapLibre の FeatureCollection は、外部から「フォントベクトルデータ」 をダウンロードしてそれを元に描画するという、大変真面目な手法で描画出 来る。 ● 欠点は、そのフォントベクトルデータは TTF とか OTF のようなメジャーな フォーマットではないので、予め変換して作っておく必要がある。 Noto Sans などは変換済みのファイルがあることはある。 ● つまり、馴染みのあるフォントを自在に指定できない上に、描画時にその ファイルをロードしなければならない(アセットの増加)。 ● https://github.com/maplibre/font-maker

Slide 47

Slide 47 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – 現実的にはどうやって解決しているか? ● 誰でも思いつく別解として、テキストをビットマップイメージとし て描画して、それをテクスチャとして貼り付ける(ベーク)する。 ● テキスト毎に画像を用意することになるので、ベークコスト・イ メージとテクスチャメモリの圧迫などの問題がある。

Slide 48

Slide 48 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – 現実的にはどうやって解決しているか? ● 中間の解決策として、文字グリフをばらばらに描画してテクスチャ に配置して、それらをつなぎ合わせて描画する。例えば ‘ M’, ‘A’, ‘P’ … というように文字毎に独立したテクスチャを作り、シェーダーで つなぎ合わせて ‘ MAP’ のテキストを描画する。 ● これなら、同じグリフが多用されると効率が良くなる。 ● 但し、一般のフォントレンダラーが考慮するような細かい調整は無 理なので、並びがガタガタになったり不自然になったり、要するに 汚い ...

Slide 49

Slide 49 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – 結局、全部愚直にベークすることにした。 – 最近の GPU はリッチで、(最先端の 3D ゲームでも無い限り)テクス チャメモリの不足などということもそれほど発生しないはず。 – 但し、ラベル描画の度にテクスチャデータ転送が発生してしまう。これ はテクスチャアトラスを使うことで軽減は出来るが、アトラスのビルド コストが別途発生する。

Slide 50

Slide 50 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – 全部ベークするなら、 CSS が理解できるフォントは全て使用可能で、 フォントレンダラの美しい描画をそのまま表示できる。 – テクスチャサイズをある程度大きくすれば、テクスチャが拡大されても 粗は目立たない(要するに品質コントロールはやりやすい)。 – HTML DOM がなくても、 OffscreenCanvas を使用すればメモリ内に描 画できる。描画したら transferToImageBitmap() で ImageBitmap に変 換できる。それを WebGL のテクスチャに突っ込む。

Slide 51

Slide 51 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 ● 実装中の問題点の推移 (5) – テクスチャアトラスとは、細かい画像を一枚の大きなテ クスチャに集約しておいて、シェーダー実行時にテクス チャバッファを切り替えるコストを削減して、 UV 座標 (テクスチャ上の XY )でアクセスするるというもの。 – 上の例のように、同じサイズの画像が整然と並べられる のなら非常に効率が高いが、実際にはサイズが違ってた りするのでまあテトリス。 By Daniel Schwen - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=7309854 By Nicolas P. Rougier - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=37910617

Slide 52

Slide 52 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – そもそもラベルテキストの長さは予測不能で、恐らく全てのラベルの長 さがバラバラなので… – 結局、ここを手当する方法は見いだせていません。なので、現状、大量 のラベルを表示させようとすると、テクスチャアトラスのビルドに時間 がかかります。 – その間完全に止まってしまうのを防ぐため、消極的手法ですが、要求を 複数回に分割してアトラスビルドを行っています(描画に反映されるの に時間がかかる)

Slide 53

Slide 53 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 ● 実装中の問題点の推移 (6) – WebGL と WASM の特性に気がつくのが遅れた… – WebGL は、 32 ビット浮動小数点のバッファ( Float32Array )でデータを 突っ込んで描画させます。このバッファを切り替えるのにもコストがかかる ので、同じバッファ内に全てのデータを突っ込んでおくのが良いです(オフ セットだけでアクセスできる)。 – WASM は、引数( n 個)と戻り値、バッファのアドレス渡しでデータの送 受を行えます。バッファは通常 Int32Array, Float32Array, Float64Array など で受け渡します。

Slide 54

Slide 54 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – 実装の初期、 WebGL と WASM の事情がわからなかったので、割と素 直なデータ構造を TypeScript 上で定義していた。例えば、 このようなスプライト画像が n 個ある (AoS: Array of Structures)

Slide 55

Slide 55 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – このデータを、 WASM で計算するために : ● render() 毎に計算データを Float64Array にコピーして WASM 呼び 出し ● バッファから値を取り出して計算 ● 計算結果を出力バッファの Float32Array にコピー ● JS 側に戻り、出力バッファの内容を WebGL の計算バッファにコ ピー ● WebGL 実行

Slide 56

Slide 56 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – まあ大変効率が悪い。更に、 SIMD 命令を効率よ く使用するには、 AoS では駄目で、 SoA (Structures of Array) でなければならない。 – SoA は要するに、 X 座標群、 Y 座標群、レイヤー 番号群、のように、要素毎に配列に集約するよう な構造で、 AoS 構造とは「逆」のような感じ。 – これを maplibre-gl-layers の全部の構造に対して 行う必要があるので、この事に気がついたもの の、もはや手遅れ ... AoS (Array of Structures) SoA (Structures of Array) https://en.wikipedia.org/wiki/AoS_and_SoA

Slide 57

Slide 57 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – とりあえず、 SIMD 計算を行わせるために、入力バッファから値を取り 出す時に SoA に並び順を変えて計算を行うようにしたところ、大きく はないけどパフォーマンスが上がったので、これで妥協。 – 出力バッファの構造も、 WebGL にそのまま突っ込むことが出来るよう なバッファレイアウトにすると最速となるはずだけど、自動回転・疑似 LOD 周りの実装が邪魔をして、今から作り変えるのはちょっと無理、と いうことになって断念。

Slide 58

Slide 58 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – ThinkPad が高速な件(想像) : Core i9-9980XE: DDR4-3200 x 4 チャネル (& 外付け GPU 転送コスト ) Core i7-1370P: LPDDR5X-7500 x 2 チャネル (& 内蔵 GPU 転送不要 ?) – データをコピーしまくってるからじゃね? データページを COW にしておいて GPU が 直接見れば超早くなるんでは ? (しらんけど)

Slide 59

Slide 59 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 ● 実装中の問題点の推移 (7) – 投影パラメータがわからない問題 – render 関数で options 引数から投影計算のためのインターフェイスは 取得できるが、細かいパラメータ(カメラの位置や角度・ FOV ・地図 中央・ビューポート情報など)の一部は見えない。 – 座標一つについて計算するなら 公開関数が簡単で良いが .... これでは SIMD 計算が出来ない

Slide 60

Slide 60 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – MapLibre 本体から、様々なパラメータを取得できる。 その中から投影パラメータを無理やり引き出す(ここだけ非常に汚い) 投影変換インターフェイス (型は公開されていないがアクセスは可能) 計算に必要なパラメータ値を 全部引き出して WASM に転送する (そしてまたコピーが増える)

Slide 61

Slide 61 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – これも、当初は JavaScript で計算処理を書いていた。バリバリの AoS 構造、 options 引数による計算も簡単なので何も考えずに使用。 – WASM SIMD 計算をやろうとした時点で問題に気がついたので、内部構 造の作り替え(リファクタリング)に非常に苦労した ... – 始めから全てを見通せていればこんな事には ...

Slide 62

Slide 62 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 ● 実装中の問題点の推移 (8) – WASM 周りで色々残念な事になって打ちひしがれていたので、 WASM マルチス レッドに手を出してみた。 – WASM マルチスレッドは、 WASM の隔離された環境(プロセス)で、共有メモ リを使用して部分的にメモリを共有することで実現している。 – (理解が正しければ)プロセス分離されてるけど、共有メモリを使用してその部 分で同じメモリをアクセスしているように見せかけることで、単一プロセス内で 動いているスレッドのように見える、ということ (?) – なんか、思い切ったアーキテクチャだ ...

Slide 63

Slide 63 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – WASM マルチスレッドは、まだ十分環境として「こなれていない」印象 を受けた : ● 分離された複数のプロセス間で共有メモリを機能させるため に、 HTML 仕様で規定されているセキュリティ制約を緩和させる必 要がある。そのために、 HTTP レスポンスヘッダに Cross-Origin- Opener-Policy, Cross-Origin-Embedder-Policy, Cross-Origin- Resource-Policy などを適用し、このページの振る舞いに問題が無 いことをブラウザに伝える必要がある。

Slide 64

Slide 64 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – WASM マルチスレッドは、まだ十分環境として「こなれていない」印象 を受けた : ● 分離された複数のプロセス間での共有メモリの橋渡しを行うため に、 SharedArrayBuffer を使用する。この共有メモリはサイズを決 定しておく必要がある(自動拡張できない)。 ● 最大スレッド数を決定しておく必要がある( Emscripten SDK の制 約かも知れない。未確認)

Slide 65

Slide 65 text

Kouji Matsui (https://www.kekyo.net/) 内部構造 – 結構制約がきつい。例えば、 HTTP ヘッダの明示は github.io でホスト するページでは実施できないので、 maplibre-gl-layers のデモページで は、マルチスレッド WASM が使えなかった。 – また、マルチスレッド化してみたけど、あまり速くならなかった(あり がち)。もっと大量のデータ(つまり大量のスプライト)を投入しない と効果を発揮できなさそうなのは分かってた。でも、他の部分の効率が 悪すぎて無理感 ... – とにかく可能な限り TS 側で計算や処理を行わないようにすることがキ モ。

Slide 66

Slide 66 text

Kouji Matsui (https://www.kekyo.net/) その他 ● FeatureCollection はイミュータブルインターフェイスだった。 maplibre- gl-layers は、いわゆるインペレーティブ(命令的)インターフェイスなの で、ギャップがあります。 なので、 React などで使う場合は、間に差分更新の為のエンジンが必要に なるでしょう。 ● 例えば、入力された FeatureCollection から、差異があるオブジェクトや フィールドを抽出して、それを updateSprite() に投入する、のようなエン ジンを書いておけば、外側からはイミュータブルインターフェイスとして見 えます。 ● React が DOM を、仮想 DOM から差分更新することに近い考え方です。

Slide 67

Slide 67 text

Kouji Matsui (https://www.kekyo.net/) 質疑応答 ● 沢山のスプライトがうごめいている、以外の色モノ説得力のないライ ブラリですが、それでもかなり良い作品に仕上がったと思っていま す。 ● 「大量の移動体を地図上でスムーズに動かす」という目的であれば、 非常に扱いやすく、かつ、十分目的を達成出来ると感じています。 ● そして、 MapLibre はとてもクールです。

Slide 68

Slide 68 text

Kouji Matsui (https://www.kekyo.net/) Before maplibre-gl-layers ● このまま行くと、 – 更新されていないしモダンじゃない leaflet を使って、そのうち何 か問題を引いてしまう – モダンで保守されている MapLibre を使うが、移動する度に画面 がフラッシュして、偉い方々に絶対突っ込まれて泣きを見る の二択が人類に迫っていた ... 今だ、今やるしかない ... この手で!