TouchDesignerのパフォーマンスチューニングの時に気をつけることと、今まで触ってみて気付いたTIPSをつらつら書いていこうとおもいます。
サンプルファイルは こちら
インストールしたら最初にやる事
Adaptive Homing by Default
をオフにする
メニューバーの、 Edit > Preference
から、Geometry
タブの中にある Adaptive Homing by Default
をオフにします。
これが入っているとSOPのプレビュー領域が常にジオメトリがカメラの範囲内に入るように計算してくれるようになりますが、裏を返せば毎フレーム全ポイントをなめていってジオメトリの中心と大きさを算出する、というものなので、ポイント数が増えてくるとプレビューだけで相当な重さになります。
そんなにジオメトリを眺めていい事もないと思うので絶対にデフォルトで切るようにしましよう。
どうしても使いたい場合はSOPを選択してViewer Avtiveにした後に右クリックメニューから有効化することができます
バージョン管理は他でやる事が多いのでバックアップは作らない
好みの問題になりますが、個人的にはバックアップファイルでプロジェクトのフォルダが荒れるのが嫌だな~… とおもうので思い切ってバックアップは作らず、git、svnなどでバージョン管理をしています。
Preference > General > Incremental Filename on Save
で Off
で切ります。
CPU Cook Time
をこまめに気にする
オペレーターを中クリックするとCPU Cook Time
又は Children CPU Cook Time
が出るので、ちょいちょい気にしながら確認するようにしましょう。
指標としては1フレームが16msで終わればいいので、単純計算で1msかかっている処理であれば16個まで増やせる、ということにまります。
無闇矢鱈と最適化に走っても逆に生産性が落ちてしまうので、処理時間とシーン内での処理の重要性を意識することが大事です
逆に言えば処理が重くても、ものすごいキーになるような計算なのであれば、プロジェクトのフレームレートを30fpsとか24fpsに設定して成立すればOKな訳です。大切なのは60fpsをキープする事ではなくて、いい感じの絵を出す事です!
プロジェクト全体のパフォーマンスを確認するには Performance Monitor Dialog
も参考になります
いらないGeometry系のプレビューを切る
色々な所で内容が表示されると便利なのですが重くなる原因にもなります。
必要ない所では、Preview、Display、Render、Templateのフラグを切っておきましょう。
個人的にはTOP、ある程度のサンプル数までのCHOPは表示しててもそこまで重くならないと思っているので放置しています
解析系のTOPは解像度が低くても言うほど精度に差がないので落とす
Trace SOP
など、一部の画像解析系のオペレーターは解像度を上げているとその分処理が重くなる時があります。
処理が重いなと感じたら入力の解像度を落として出力にどの程度の影響が出るかを見極めるようにしましょう。(大抵のOpenCV系の処理は解像度落としてもそんなに影響ないです)
クオリティの低下が許容内であれば軽い方がいいですからね…
Blur TOPのPre-Shrink
は見た目に問題がないレベルまで下げる
TouchDesignerだとブラーも簡単にかけれるので多様してしまいがちですが、GPU負荷の高い計算である事に変りはありません。
Filter Size
の数値を上げるとボケの半径が大きくなりますが、その分処理負荷が高くなります。その時にPre-Shrink
の数値を上げると1/N倍にダウンサンプリング → ブラー処理 → N倍にアップサンプリングというような処理をしてくれるので、結果的にブラー処理のピクセル数が削減でき、パフォーマンスが上がります。
絵的には Filter Size
でボカした方が綺麗なのですが、そこまで綺麗にぼかす必要がないのであれば可能な限り Pre-Shrink
のほうでボカすようにしましょう
ちなみにExprementalのほうだとオペレーターを中クリックで GPU Cook Time
も表示されるようになっているので、GPU側の最適化もやりやすくなっています
数が必要な場合、Copy SOP
ではなくてGeomerry Instanceを使う
/project1/COPY_SOP_vs_GEOMETRY_INSTANCE
を参照
個人の感想ですが、TouchDesignerだとオブジェクトの数を出そうとするとSOPで対応するのは結構キビシイなと感じでいます…
100個の球体を複製という処理をCopy SOP
とジオメトリインスタンスを使った方法と比較するとパフォーマンスに60倍の開きがありました
表現の幅もジオメトリインスタンスを使ったほうが広い事が多いので、Copy SOP
の出番は見付ける方が難しいのではないかと思います…
Script CHOPとConstant CHOPの使いわけ
/project1/SCRIPT_CHOP_vs_CONSTANT_CHOP
を参照
CHOPの値に簡単なエクスプレッションを書き込みたい、とか、他のCHOPを参照した値を取り出したいといった時にScript CHOP
とConstant CHOP
のどちらを使ったらいいか?というものです。
個人的には扱うCHOPのデータが1サンプルの時にはどちらでもいいのかなと思いました。軽く検証してみましたが、1サンプル程度のデータであればパフォーマンスはどちらも変わりありませんでした。
ただ、マルチサンプルのデータを扱うとなるとConstant CHOP
では処理できないのでScript CHOP
になると思います。
似たようなものとしてExpression CHOP
がありますが、僕は全然使ってないです。
Pythonのあれこれ
op(NAME) や op(NAME)[CHANNAME] の文字列ルックアップは地味に重いので可能であればキャッシュする
/project1/CACHE_CHANNEL_OBJECT
を参照
CHOPのオブジェクトはPythonから、チャンネルインデックス、又はチャンネル名で参照できます
my_chop = op('my_chop')
my_channel = my_chop[0] # index access
my_channel = my_chop['chan1'] # string access
ですが、簡単に計測した所、チャンネル名とインデックスのアクセスを比較するとチャンネル名のほうが6倍ぐらい遅いです
なので、必要であればルックアップしたオブジェクトをキャッシュして使います。
chans = [] # グローバルレベルに適当な配列を用意する
def onCook(scriptOp):
input = scriptOp.inputs[0]
if not chans: # 配列が空の場合キャッシュする
for i in range(input.numChans):
chans.append(input['chan%i' % i]) # ここでは名前アクセスでもOK
scriptOp.clear()
scriptOp.numSamples = input.numChans
scriptOp.appendChan('chan1')
for i in range(scriptOp.numSamples):
scriptOp[0][i] = chans[i][0] # キャッシュした配列にアクセスする
キャッシュした副作用として、入力のチャンネル数が変わると参照が外れてバグの原因になるので、キャッシュをクリアする仕組みを作るか、チャンネル数が変わった場合カット/ペーストで強制的にオブジェクトを作り直すか、などの対応が必要になるかと思います。注意
cProfileでスクリプトの実行をプロファイルする
/project1/USE_CPROFILE
を参照
Python側でやる事の規模が大きくなってくると、TDのプロファイラではPython側の処理が補足できないので、どこで時間がかかるかを見極めるのが難しくなります。
そういう時はPythonのcProfile
モジュールを使ってプロファイルしましょう。
適当なText DAT
を作って以下のようなコードを右クリックからRun Script
、または選択してCtrl+R
してみましょう
import cProfile
pr = cProfile.Profile()
pr.enable()
for i in range(1000):
mod('execute1').onFrameStart(None) # ここで計測したい関数を実行
pr.disable()
import pstats
stats = pstats.Stats(pr)
stats.sort_stats('tottime')
stats.print_stats()
上記の例だと1000回関数を実行した時にどれぐらいの時間がかかっているか、などの情報が表示されると思います。
さらにその関数の中でも、どの部分にどれぐらいの時間がかかっているか等も表示されるのでやみくもに最適化をするよりもかなり時間が節約できます
何でもないSOPが異常に重い
/project1/WAITING_DISPLAY_SYNC
を参照
たまに、CPU Cook Time
を見ると何もしてないSOPが10-14msくらいかかっている時があります。
これはレンダリングがディスプレイの同期待ちでブロックしている時間だと思われるので特に気にしなくても大丈夫だと思います
ジオメトリをまとめる
ネットワークが同一改装で横にひろがると、表示すべきオペレーターが多くなってしまうのでパフォーマンス的にも構造的にもあまりよくありません。なので、適当な意味的なまとまりでグループを作るのはいいアイデアです
↑ のgifは右クリックからCollapase Selected
で複数のオペレーターをBase COMP
に入れた後にChange COMP Type
する事で、複数のジオメトリをまとめて単一のジオメトリにする操作です。
ジオメトリをまとめると、親のほうでトランスフォームやマテリアルを変更すると子のほうも一気に適応されたりなどとても使い勝手が高いので、覚えておいて損はないテクニックだと思います
イベントと操作を分離する
/project1/SEPARATE_EVENT_AND_OPERATION
を参照
Execute系のDATは結構よく使いますが、たとえばExecute DAT
でonStart
のイベントで何かしたいと思った時にテストするのにTouchDesignerの再起動が必要になったりしてとてもめんどくさい時があります。
そういう時はイベント系のDATからは、別のText DAT
のスクリプトをキックするようにしましょう。
op('MY_TEXT_DAT').run()
のような感じです。.run()
には引数も渡せます 詳しくはこちら
Text DAT
は他のExecute系のDATに比べて簡単に編集できて、Ctrl+R
で簡単に実行できるのでテストの効率がよく、開発時間の短縮とバグ率の低下が期待できると思うのでオススメです
OSCを受ける時のTIPS
OSC In CHOP
の注意点
/project1/DROP_OSC_FRAME
を参照
TouchDesignerではOSCを受信するのにCHOPとDATの両方がありますが、1フレームのみトリガーなどのメッセージを受ける時にはOSC In CHOP
はオススメできません
理由は、処理が重くなったりしてTD全体のFPSが下がってしまった場合にCHOPのCookの処理が走らなくなる時があるという物なのですが ↓ のgifを見てもらったほうが早いと思います
Hog CHOP
で仮想的にCPUリソースを食いつぶした時にどういう挙動になるかという奴なのですが、デフォルトのままのOSC In CHOP
の設定だとPulseをとりこぼしているのがわかると思います。
Queued
の設定を入れるとちゃんと動いているように見えますが、若干レイテンシが発生するようです (ここのあたりあまりわかっていません… レイテンシが解消されればそっちのほうが楽なので、知ってる方教えてください!)
なのでOSCの入力を使うのであれば OSC In DAT
のほうを使うのをオススメします。こちらの場合はCookの間に入ってきたメッセージが配列として取得できるので、ユーザー側で任意を処理を入れることができます
ただ、上記の問題は1フレームのみの取りこぼすと大変な事になる系のトリガーの話なので、センサーからの連続量をとりたい等の用途であれば普通にOSC In CHOP
で必要十分です。
そのへんはケースバイケースですが、念のため Hog CHOP
を走らせてみてFPSが落ちてもちゃんと動作するかの確認はしておいた方がいいと思われます
人生を左右する1パケットもありますので、取りこぼさないようにしたいですね!
OSC In DAT
のメッセージをルーティングする
/project1/ROUTE_OSC_DAT
を参照
OSC In DAT
を普通に使っていると長々としたif - elif大会になってしまうので、外部のTable DAT
を参照してルーティングのテーブルを作ってしまおうというアイデアです
さらに前述した外部Text DAT
呼び出しのパターンを組み合わせるとさらに見通しがよくなりますね
なるべく参照ではなく接続を使う
これは完全に好みの問題になってしまいますが、個人的にはSelect
系のオペレーターを使った参照や、エクスプレッションでop(NAME)
を使った参照を多様すると、だんだんネットワークが読みづらくなり、これ消してもいいよなと思って考え無しに消すと実は色々な所で参照されていて盛大にバグる、みたいな事が起きやすいなと思っています。
なので、参照を使う場合はできるだけ同一階層内に留めておいて、階層をまたぐ場合はそれぞれIn
、Out
を使って接続する、全体から参照したいグローバルなオペレーター(通信系、デバイス系) は、/GLOBAL
みたいな階層を作ってそこに絶対に配置するようにする、といった工夫をするだけでシステムの堅牢度が上がるのではないかと思って最近はネットワークを組んでいます。
まとめ
TouchDesignerは非常によくできた環境で、素早くクオリティの高いアウトプットができますが、ある程度ソフトウェア設計的な考え方を取り入れてネットワークを組んでいくとさらに生産性が上がるのではないかと思います。
ビジュアルプログラミング環境でのソフトウェア開発哲学のようなものは ここ に非常によくまとまっているので一読をオススメします
長々とあまりまとまりのない記事になってしまいましたが、このあたりで!