刻みTD：複数toe化によるシステム最適化と分散開発

この記事は[TouchDesigner Advent Calendar 2021](https://qiita.com/advent-calendar/2021/touchdesigner)の21日目の記事です。

## 刻みTD

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/b92b6f13-54fc-6d03-acbb-24ff0cfeb070.png)


**刻みTD (きざみてぃーでぃー、きざみたっち）**とは：**toeで構築する1つのネットワーク全体を、個別モジュールのプロセス間通信に置き換える（toeを刻む）**ことにより、**システム全体の開発性と安定性、パフォーマンス向上**を試みる。というものです。さらに記事の後半では、**刻みTDをすることによる複数モジュールの複数人同時開発**に関しても紹介いたします。
> なお、この名称はチーム内で勝手につけているものなので、公式でもなんでもないです

本記事で使用しているサンプルファイルは[こちら](https://github.com/shks/ChoppedTD)にあります。

# 刻みTDの作り方

TouchDesignerでは、`Base COMP`や`Container COMP`のように全体のネットワークの一部分を機能モジュールのようにまとめることができます。Componentとしてまとめた上で、`In - Out Operator`を用いることで、Componentごとをネットワークで接続して、より抽象化した状態で、機能モジュールごとを接続して開発をすることができます。さらに、個別のComponentに対して、`Component Extention`によってスクリプト処理を追加することで、複雑な処理を内包する場合でもシンプルに１つの機能モジュールとして扱う事ができます。このComponentによってモジュール化を行い、モジュール同士をネットワークで接続して全体のシステムを構築することで、全体として見通しの良いシステム構築ができると思います。

一方で、システムが大きくなるほど、1つのネットワーク(toe)で複数の処理を同居させた場合に、一部の処理が全体のパフォーマンスを律速してしまう、一部の処理が止まってしまうとシステム全体を止めざる負えない、などの問題が発生します。TouchDesignerでは、基本的には1つのtoeで実行されるシステムは1つのプロセスで実行されます。

そこで、toeで構築する1つのネットワーク全体を、複数のtoeに分割することで複数のプロセスに分割し、個別モジュールのプロセス間通信に置き換える（toeを刻む）ことで、システム全体の開発性と安定性、パフォーマンス向上を試み、さらに複数人開発も容易にする。という設計を可能にします。本記事では、**刻みTDの特徴、基本的な刻み方、刻みTDによる開発**に関して紹介したいと思います。


## 基本的な刻み方

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/6fdb2cb3-b878-05fa-c92d-57dca8f43c23.png)

非常に単純な例として、モジュール`A→B→C→D`のように処理の結果を順次渡していき、最終的な画面表示や外部モジュールの動作を行うシステムを想定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/102275d4-6072-2901-9997-ae5a97beb09f.png)

通常のTouchDesignerのネットワークでは、図にあるように、A→B→C→Dの機能ごとにネットワークをつくります。かつ、それぞれの処理モジュールをコンポーネントにまとめることで、視覚的にもA→B→C→Dもモジュールがわかりやすくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/ef46799b-78dc-d055-c92f-1e496369c371.png)

> F2で表示して、パフォーマンス実行状態を観察しても、コンポーネントA, B, C, Dが一連のプロセスとして処理されていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/90902355-546e-70a9-d10e-20450390a6f5.png)

ここで、それぞれのコンポーネントごとをつなぐリンクの代わりに、`TouchOUT`, `TouchIN`などのプロセス間通信を用いることで、toeを刻みます。具体的には、コンポーネントA→B→C→Dで構成されていた、システムをそれぞれ、`A.toe`, `B.toe`, `C.toe` and `D.toe`のように別のtoeとして作成し、各toe間を`TouchIN-OUT`で繋ぎます。リンクの代わりに、別の`TouchIN/OUT`を構成する必要があるので、それぞれ異なるポート番号を割り当てます。ここでは、`A→(8001)→B→(8002)→C→(8003)→D`としています。
これにより、`A.toe`, `B.toe`, `C.toe` and `D.toe`はそれぞれ別のプロセスとして動作したうえで、処理内容は`TouchOUT-IN`で伝搬していくのでシステム全体としては`A→B→C→D`を一つのtoeで構成していたときと（ほぼ）同じシステムを構築することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/399ad4cb-8e8c-2a8a-3cd8-8d7fc1894248.png)

>  それぞれのパフォーマンスモニターをみると、先述のコンポーネントA, B, C, Dが一連のプロセスとして処理していたのに対して、4つのtoeに処理が分割されていることがわかります。

## 刻みTDによるの利点と欠点

刻みTDの利点は下記になります。

- 別プロセスとして**処理負荷を分散**することができる。
- 個別モジュールにすることで、**ユニット開発やテストが容易**になる。
- 個別モジュールごとの**version管理**が容易になる。
- 個別モジュールの**フレームレートを可変、個別指定**できる。

一方で、刻みTDの欠点も留意する必要があります。

- プロセス間通信の際に発生する、**処理・通信遅延**が発生する。刻むごとに、プロセス通信とアプリケーションフレームの関係で、システム全体の遅延が蓄積する。
- **モジュール間のやり取りする内容を変えるのに手間**がかかる。（システム全体の中で、何の情報をどこのコンポーネントが用いるかが明らかでない場合には、刻むと大変）
- モジュール間での**処理の実行タイミングが不定**になる。

このように、盲目的に刻むのではなく、システム全体や開発状況に応じて、まとめておくか、刻むかを判断する必要があります。なお、刻んだ後に繋ぐ、といった方法も後述いたします。

- 刻みTDによる効果が期待されるユースケース
    - 一連の処理の中で、処理負荷が高くアプリケーション全体のフレームレート低下や極端なフレームドロップ発生を防ぎたい。
    - システム一連の中で、描画フレームレート以上（例えば60Fps以上)の処理フレームレートで実行したい。
    - システム一連の中で、可変フレームレートでの処理と固定フレームレートでの描画を実現したい。
    - 処理負荷分散や、機材物理的制約から複数のマシンで一連システムを構築したい。
    - 複数モジュールを複数人で同時開発しつつコンフリクトを避けたい。
    - システムの一部が不安定になったとしても、システムの全体は止めずに部分的にリカバリー可能にしたい。


このような場面において、刻みTDは効果を発揮することが期待されます。

## 刻むためのプロセス間通信：TouchIN/OUT

TDを刻む場合、`TouchIN/OUT`などのプロセス間通信を用いることになります。しかし、プロセス間通信を用いる以上、その処理や入出力タイミングのズレによって発生する遅延に注意する必要があります。例として、`Queue`数や`Maximum Queue`を調整することで、遅延を最小化することもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/c6a27711-77a7-1ec5-bdfc-42d0bfb16188.png)

参考：
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">刻みtoeでBuffer最小のTouchOUT-IN / TouchOUT-INで往復させた場合、欠損があるが安定すると1フレーム遅延で帰ってくる。いいかも。刻みtoe TT作戦 <a href="https://twitter.com/hashtag/TouchDesigner?src=hash&amp;ref_src=twsrc%5Etfw">#TouchDesigner</a> <a href="https://t.co/5MAuLMr1xK">https://t.co/5MAuLMr1xK</a> <a href="https://t.co/Y1bm6d7OPm">pic.twitter.com/Y1bm6d7OPm</a></p>&mdash; Shunichi Kasahara (@shn_kasa) <a href="https://twitter.com/shn_kasa/status/1447236684223959041?ref_src=twsrc%5Etfw">October 10, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


一方で実行環境によっては値抜けなど発生するので、実行環境でテストしたほうがよいです。
参考：https://forum.derivative.ca/t/touchin-touchout-lag/4674/7


### TouchIN/OUT CHOP, DAT, TOP
- `TouchIN/OUT CHOP`では、CHOPオペレーターで扱うような数値情報をプロセス間でやり取りできます。`TouchIN/OUT CHOP`では、複数チャンネルの情報も送受信できますが、１サンプル以上のCHOPデータを送る事ができません。つまり、音声データをやり取りするのは現時点では向きません。（記事作成時点）しかし、DATデータにへ変更することで1サンプル以上の系列のデータを送受信できるようになります。
- `TouchIN/OUT DAT`では、DATオペレーターで扱うようなテキストデータや、テーブルデータをプロセス間でやり取りできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/0be8a3e9-d624-4a79-3021-35f5c8ae35f5.png)

- `TouchIN/OUT TOP`では、TOPオペレーターで扱うようなテクスチャデータをプロセス間でやり取りできます。CHOPやDATと異なり、TOPではテクスチャデータを（`HapQ`などの圧縮、もしくは非圧縮）送受信するため高負荷が予想されます。まず、同一マシン内であれば、`Spout (Windows)` もしくは`Syphon (Mac)` のテクスチャ共有を使用する選択肢を検討したほうが良さそうです。

### 他のプロセス間通信
`TouchIN-OUT`以外にも、プロセス間通信を実現する方法は`NDI`, `OSC`, `Spout`, `Syphon`などがあります。特に音声、映像以外の数値やテキストのやり取りであれば`OSC`、映像であれば`Spout`, `Syphon`（同一PC内）や`NDI`などがあり、TouchDesigner以外のアプリケーションや開発環境においてもサポートされているので、それらを組み合わせることで、刻みTDの中に`Unity`や`P5`,独立の`Python`処理などを添える、といったシステムを構成することができます。

## 刻みTDを複数マシンで処理分散
刻みTDによってプロセスを分けることで、同一PC内で刻むだけではなく、複数PC間に刻んだTDを分散させることが出来ます。複数PCをネットワーク（有線LAN）で接続したローカルネットワークを構成した上で、先述のIP指定で`localhost`の代わりにそれぞれのPCでのIPアドレスを指定することで、複数マシンへ容易に処理分散を行うことができます。図の例は、1台目のPCで信号処理などをしたうえで、2台目のPCでフレームレートを犠牲にせずに映像を表示したい場合などの構成例です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/2430b23c-bdad-dda6-119f-f790ef20d2a4.png)

一方で、複数マシン間にする場合には途中経由のネットワーク機器の性能や、各PCのネットワーク設定（Windows Firewallに注意）によって遅延が発生する必要があります。特に、`TouchIN/OUT`のParameter設定で、`Queue`を極端に小さくしてしまうと値の取りこぼしが発生する経験がありました。複数マシンで刻む場合には、機材状況や`Queue`の設定を注意深く検証することをおすすめいたします。


# 刻みTD開発

## 背景：開発フェーズと最適化フェーズ
刻みTDの必要性がでるような複数のモジュールを用いるシステム開発の場合には、それぞれのモジュールの機能開発やデバックをする開発フェーズと、全てのモジュールを繋ぎシステム全体のパフォーマンスをチューニングする最適化フェーズが存在すると思います。それぞれのフェーズでは異なる要件が発生します。
### 開発フェーズ

- 個別のモジュールをそれぞれの担当者が並行的に開発を行いたい
- 個別のモジュールのみでデバックを行いたい
- 物理的な装置を動作させる代わりに、シミュレーションや記録データを用いて開発をしたい
- 並行開発でコンフリクトをできるだけ避けたい
- 全体のシステムを動かしながら、個別のモジュールを入れ替えて開発検証したい。

### 最適化フェーズ

- システム遅延を最小化したい
- フレームレートを安定させたい
- システム起動を含めた運用安定性を上げたい

## 刻みTDと繋ぎTD

刻みTDによるプロセス分割によって、**処理負荷分散や並行開発**を行ったのちに、刻んだTDのプロセス間通信で発生する遅延を解消し、**システムを最適化したいフェーズ**へ移行する場合も想定されます。そこで、刻みTDに加えて繋ぎTD：モジュール分割化させたtoeを再度統合したtoeにする。ことで、**開発フェーズ**と**最適化フェーズ**を必要に応じて柔軟に進行することを可能にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/90902355-546e-70a9-d10e-20450390a6f5.png)


- ここでは、上述の`A.toe`, `B.toe`, `C.toe` and `D.toe`のように刻んだTDを事例にします。ここでは、下記のような要件を想定します。
    - A:可変フレームレートシステムで、別プロセスとして動かしたい。
    - B-C : 処理不可は高くないが間で発生するプロセス間遅延を最小化したい。
    - D:外部機器制御上、可能な限りシステムを安定に、60FPSを担保したい。

この要件に基づき、**B-Cのモジュールを再度TouchDesignerの標準のリンクでシステムを繋ぐ**とします。しかし**同時に、BとCのモジュール自体も並列・個別に開発検証を行いたい。**とします。

### tox化

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/b2b25036-2508-9d70-9f88-b5539e58536e.png)

そこで、`B.toe`の中にある、Bモジュールコンポーネントと`C.toe`の中にあるCモジュールコンポーネントをそれぞれ、`B.tox`, `C.tox` として、外部モジュールファイル化します。tox化は例えば、コンポーネントを右クリックメニューで`Save Component tox.`をすることで、別toxファイルとして保存できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/0b366a6c-ba36-c0a1-85bc-0bba64e32b46.png)

例として、`A.toe`, `B.toe`, `C.toe` and `D.toe`すべてをtox化した場合には、図のような関係になります。

### tox化の際のTips

tox化する際に、`TouchIN/OUT`の別プロセスのためのオペレーターを、コンポーネントにInオペレーターの第2インレットに接続することで、`TouchIN/OUT`でも、実際のリンクでも両方使えるtoxにすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/887e4b95-ea58-6ca9-71e7-d4dc2e21c3a2.png)

Inオペレーターの第2インレットは、「コンポーネントの上の階層でインレットへの入力がない場合には、第2インレットの入力を用いる」という挙動をします。つまり、コンポーネントを実結線すればそのリンクの情報を用いる。もしコンポーネントへのリンクがなければ代わりに、`TouchIN/OUT`の情報を用いる。という事ができるのです。


### 再繋ぎTD

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/5bbca70a-45c3-762c-ed62-8eae45373646.png)

それぞれのコンポーネントがtox化された後、B-Cモジュール統合のためのtoeを`BC.toe`として用意し、そこに`B.tox`, `C.tox`を配置します。ここでは、`A.toe`と`D.toe`はそのままにしておきます。

このようにすることで、A, Dモジュールとは分離しつつも、BとCモジュールを再び通常のリンク結線に戻し、刻んだ事によって発生してた遅延を解消することが出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/b0f9bd14-4ea5-7bf7-3bcd-3b4f958c3d2f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/85a1b14c-36a7-0f73-e3c7-58475f44f4fe.png)
また、全てをリンクによって結線する。完全に繋ぎTDに戻す、ということも可能です。ネットワーク図としては、最初にコンポーネントで分けた状態と同じに見えますが、それぞれのコンポーネントがExternal toxになっていることに注意してください。ここでの、ファイル構成は図のようになります。

## 刻みTD開発

- 開発をすすめる上で、BモジュールとCモジュールをそれぞれ更新していく場合、`B.toe`、 `C.toe`、さらに、`BC.toe`でそれぞれ`B.tox`, `C.tox`に該当するコンポーネントを編集することは出来ますが、あくまでもそれぞれのtoeの中での更新されているだけで、モジュール外部ファイル(tox)は古くなっていってしまいます。これらは、**手動でtoxを更新することで、同期を確保**することが出来ます。
- しかし手動では、**忘れやすい人間の特性**上望ましくないので、これらを継続的に開発しながら、最新の状態に保つために、下記のようなtoe, toxの取り扱いを設定しておきます。
    - `B.toe`、`C.toe`、`BC.toe`において、該当のコンポーネントを`B.tox`, `C.tox`を読み込む`external tox`とすることで、起動時に必ず最新の`B.tox`, `C.tox`をファイルから読み込まれるようになります。
    - Higaさんの`external tox saver`を用いることで、toe内に配置されている、external toxコンポーネントをtoeでの編集内容をそれぞれにtoxに保存されるように指定できます。[https://satoruhiga.gumroad.com/l/ExternalToxSaver](https://satoruhiga.gumroad.com/l/ExternalToxSaver)
    - これを行うことで、`B.toe`、`C.toe`、`BC.toe`に配置されている`B.tox`, `C.tox`のexternal ojectは、どのtoeから編集をしていても更新されるようにしておくことが出来ます。
    
- 以上の手順によって、`A.toe→BC.toe→D.toe`のように刻んだTDを一部繋ぎ直す事ができました。なお、前述のBモジュールコンポーネント、Cモジュールコンポーネント (`B.tox`, `C.tox`)では、それぞれ`TouchIN/OUT`も残っているので、個別の`B.toe`, `C.toe`を個別起動したとしても一連のシステムは処理されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70810/f25adf87-4c09-a149-1ff6-95e259c6b037.png)

このように、開発フェーズと最適化フェーズを行き来できるのと、システム全体を複数人で開発している際に、開発モジュールの担当さえ明示しておけばよきせぬコンフリクトを避ける事ができます。

# Case Study

最後に刻みTDで開発をしたいくつかのプロジェクトを紹介します。

- [こちらのプロジェクト](https://twitter.com/hashtag/ParallelPingPong)では、カメラトラッキングと三角測量によるボール3次元トラッキング、弾道予測、ロボットアームの動作計画、ロボットアームへの動きの司令、Virtual RealityHMDへの映像のレンダリングを2台のPCと7個のtoeに刻むことで、システムを安定化させています。特に、ボールトラッキングは状態に応じて、70FPS-120FPSの可変フレームレートであるのに対して、ロボットアームへの制御信号や、Virtual Reality HMDへの映像レンダリングはFPSを可能な限り固定で保つ必要がありました。また、開発途中では、実際の環境を使わずに、レコーディングデータや物理シミュレーションを用いて開発するなど、刻むことで実データとシミュレーションデータを切り替えて開発できるような構成を作っています。
- また、[こちらのプロジェクト](https://shunichikasahara.com/portfolio/morphing-identity/)では、一部の映像合成にpython側での処理を行っており、その際に著しいフレームドロップが発生するため、その部分を刻んで外部化して`TouchIN/OUT TOP`で接続しています。詳しくは、[こちらの技術紹介](https://mediaambitiontokyo.jp/en/movie/st8vfuf8o/)にて構成を紹介しております。

# トラブルシューティング

- なお`TouchIN-OUT`では、`TCP/IP`（もしくは`UDP`）通信を用います。Windowsではネットワーク設定上通信がうまくいかない場合があります。おかしいとおもったら、`Windows Firewall`を無効化してしてみてください。
- `Windows Firewall`の恐ろしい点として、完全に接続できなくのではなく、若干遅い、時と場合によっては繋がる、のような挙動で問題の切り分けに時間を要したケースがありました。怪しいときには、`Windows Firewall`を疑ってください。

# まとめ

この度は、TouchDesignerで複数モジュールを複数人で同時開発する際にわりと調子がよかった構成として刻みTDを紹介させていただきました。今後、TouchDesignerをお使いの皆様が各所で「ここの部分刻んどいて！」と言ったときのリファレンスになれば幸いです。

いつか誰かの役に立ちますように。