Clover SCと描画遅延に関する考察
この一ヶ月ほど開発リソースのすべてを傾けていたClover SC (スタイラス・チェッカー) がようやく完成に近づきました。
Clover SCは端末の、特にペイントアプリ利用シーンでの描き心地に影響する性能について分析し、客観的なデータを出力するためのツールです。
これまではぼんやりと、なんとなくこの端末は描き心地がいい/悪い、という感覚でしか語れなかった部分を、Clover SCは客観的な数値として表示できるようにすることで、簡単にペイントアプリで利用した時の端末の優劣を評価できるようになります。
具体的には以下のような機能を備えています
・描画遅延の測定用ガイドアニメーション
・タッチパネル/スタイラス座標イベント時間分解能(pps/スキャンレート)の実測値の表示
・タッチパネル/スタイラス座標イベント時間分解能の安定度(標準偏差)の実測値の表示
・タッチパネル/スタイラスが対応している機能をタッチイベントから解析
(同時押し点数・ボタン・ホバー・筆圧段階・サイズ・傾き・方向・距離等)
・Clover Paintのストローク描画を使った、ペイントアプリ利用シーンに特化したベンチマーク
・タッチパネル/スタイラスから送られてくる座標イベントのリアルタイム表示
・遅延の少ない(Clover Paint比)ペンストローク描画のサンプル
・ポイント予測付きストローク描画(超低遅延ストロークのサンプル)
ベンチマーク等は他に類似ソフトが多くあるので意味がわかりやすいのですが、特にClover SCが問題にしている「遅延」に関してはあまり馴染みのない人が多いと思われます。
ネットで「遅延」を話題にしている人でも、CPUやアプリを単純に高速化すれば遅延が少なくなると思っている事がかなり多いようです。しかし、それは誤解です。遅延には様々な要因があり、CPUの速度やアプリはその要因のごく一部に過ぎません。
そのような誤解を解き、どうして遅延を測定するこんなアプリが必要なのか、遅延の少ない端末が登場すると、なぜ描き味が向上するのかを理解してもらうために、いまこの記事を書いています。
多くの人が問題意識を共有し、Clover SCを利用して遅延が大きく、不安定な端末はダメ端末、遅延が少なく安定した端末は素晴らしい端末だということをネットで広めれば、必ずメーカーに対するプレッシャーになり、Androidタブレットでも低遅延・安定性を重視した端末が多く発売されるようになると信じています。
Clover SC開発の経緯
ツイッターのTLを調べてみると、私がClover SCを最初に作りたいと思ったのは2月上旬のようです。2月10日には時間分解能という言葉を使っていて、いまのClover SCの開発意図を完全にイメージしていた事がわかります。実際に作り始めたのは4月末からですが。
なんかいまスタイラスペンチマークを無性に作りたくなってる(単なる逃避)
— Clover Paint (@Clover_Paint) February 5, 2013
次点でスタイラスの時間分解能とかマニアックなスペックベンチアプリ
— Clover Paint (@Clover_Paint) February 10, 2013
ただ、2月上旬というと、Clover Paintの「ナビゲーション・ビュー」作成の真っ最中、その後も「PSD入出力」や「変形ツール」など、要望の多い機能の実装が控えていて、作る暇がなかったようです。
その後、スタイラスベンチマークの事を忘れかけ、キー設定が自分の中で盛り上がって作っている最中に、突如あらわれたのがenchantMOONという国産のデジタイザ付き端末でした。詳しくはネットで調べてもらえばわかりますが、私のツイッターのTLはデジタル絵描きの人が多いため、必然的にデジタイザーを搭載した端末の発表に関しては盛り上がる傾向があるのですが、この端末は紙のような描き心地を目指した、低遅延を実現するためにはAndroid OSが邪魔だったので独自にカスタマイズしたOSを作ったなどと、ペンで直接画面に描き込む液晶タブレットでは非常に気にする人の多い部分について、革命を起こすような端末である・・・と感じた人が多かったようです。私も前述のように時間分解能に関するベンチマークを作りたいなどと考えていたので、同じようなことを考えている人がいる!と、俄然盛り上がりました。
enchantMOONタッチ&トライイベント当時のTLです。
http://twilog.org/Clover_Paint/date-130423
見ての通り、私が触った感触では、期待していたようなものには程遠い内容だったわけですが、それでも、そのコンセプトに共感した人がネット上で非常に多く、予想以上の反響があったということは事実です。それを目の当たりにして、「やはり2月上旬頃に考えていたようなベンチマークを作ることには意味がある、ついでにClover Paintでは無視していたペンストロークの遅延について、出来る限り小さくする実験アプリにしてみよう!」となったわけです。
http://twilog.org/Clover_Paint/date-130424
この日はTwitter上でペイントアプリの描画遅延に関する考察を行なっています。このブログ記事では、ここで話した内容をさらに詳しく突っ込んで説明していますが、Twitter上でしか話していない部分もあるので、ツイッターにも目を通しておいても無駄ということはないと思われます。
ところでClover SCを作るにあたり、遅延を極限まで短縮したペンストロークを作成することを一応思いついたものの(時間分解能を高めることで、より正確な筆致を再現できるようになるという部分には確信がありましたが)本当に遅延をなくす事にそれほどの意味があるのか、この時点では私自身確信が持てませんでした。自分自身が欲しいと思わないものは、モチベーションが保てないため、作らないというのが、私の開発スタンスです。
遅延のない描画を試したいだけなら紙にペンで描けばいいのですが、いまいちタブレット上でどういう感覚になるかピンと来なかったので買ってみたのがこれです。
というわけで昨日言ってた贅沢品が届きました pic.twitter.com/5ekbW5kmrp
— Clover Paint (@Clover_Paint) April 28, 2013
これはブギーボード4という、一種のホワイトボードのようなものです。
これは普通の液晶等とは違い、バックライトや自己発光をしません。初期状態では真っ黒ですが、ペン等で触れると圧力により表面の液晶が化学変化して反射色が変わり、文字等が描けるというしくみになっています。ボタンを押すと電気が流れ、画面全体が初期の真っ黒状態に戻ります。
まあ昔あったレバーを引くと書いたものが消えるおもちゃのようなものですが、これが実に描き味がいい。これを使うことで、タブレット上で遅延の少ないペンストロークの実現には価値があると、自分自身納得することができました。
しかしBB-4には色々と欠点があります。
・黒背景に薄緑色の文字しか描けない
・部分的な消去ができない
・Undoができない
・反転もできない
・セーブができない
一方で長所は以下のとおりです
・軽い(100g)
・バッテリーが長持ち(電池一個で5万回消去可能)
・描画遅延が皆無
・書き味のいいペン特性
Clover SCはブギーボードの欠点を無くし、それでいて出来る限り遅延を無くし、ブギーボードのような書き味のいいペン特性を目指すことに決めました。(軽さやバッテリーに関する問題はハードウェアの進歩を待つしかありませんが・・・)
Clover SCのストローク描画サンプルの主な仕様は以下のとおりです
・基本は白背景に黒文字(白黒反転可能)
・左右反転
・全体消去
・Undo/Redo
・消しゴム
・遅延を極小にする補間前描画(後に詳述します)
・ハード特性による遅延を擬似的に無くすストローク予測(後に詳述します)
・ブギーボードに比べ少し硬めのペンストローク
(筆圧と速度により主に太さが変化するタイプのブラシ)
Clover Paintと比べると主に以下の機能を省いています
・ブラシの太さやその他パラメータの変更
・レイヤー機能
・キャンバスの移動・スケール・回転
ブラシの変更やレイヤーがないのは、 インターフェイスを単純化し、アイコン等を排除するためです。(むろん処理落ちが起きるような、重く複雑なブラシ演算処理になるのを未然に防ぐ効果もあります)
また、スケール・回転等がないのは、Clover Paintのようにリアルタイムで高速なスケール・回転を実現するためにはOpenGLを利用した描画処理が必要なのですが、機種依存の激しいAndroidの場合、どうしてもダブルバッファにしなければOpenGLを利用することができないと判断しました。しかし、ダブルバッファを使うとどうしてもその分の遅延が発生します(後に詳述します)。ダブルバッファによる遅延が他の方法で回避できるという保証もないのですが、遅延を極小にするということが至上命題である以上、この時点で諦めるという選択肢はありえないため、この機能も排除しました。
要するにClover SCのペンストローク描画は、やり直しと反転ができ、スクリーンショットの撮れる(セーブは未実装)ブギーボードを目指す、という今の形に落ち着いたわけです。
@Clover_Paint モノクロでundo有、5インチ以下は筆圧で太さと濃さが僅かに変化するペンと消しゴムのみ、10インチ以下は描くペンと塗るブラシが独立した1レイヤーでツール切り替えると連動してレイヤーも変わる、ペンは黒白二種太さ一種、ブラシは太さ色変更有で
— 安倍吉俊 yoshitoshi ABe (@abfly) April 29, 2013
Clover SCの仕様策定に関しては、安倍吉俊様のこちらのツイートも示唆に富んでいて一部参考にしています。エゴサーチする人と知らなかったとはいえ、少々失礼な物言いをした私の質問に丁寧に答えて下さったことに感謝致します。
Androidタブレットの遅延について
描画遅延には様々な要因が考えられますが、大まかに分けると以下の4つに分類できます。
- ペン入力から、アプリにそのイベントが到達するまでの遅延
- ストローク演算処理による遅延
- 画面演算による遅延
- 画面(モニター)表示の遅延
このうち、2だけがアプリケーションに起因する遅延となります。
結論から言うと、私の試したAndroid端末(GALAXY Note 10.1等)では2の処理は(処理落ちが起きることがないような細いブラシの場合)Clover Paintで50ms~70ms程度ですが、これがClover SCでは10ms前後程度になりました。ただし、依然として見た目の遅延はClover Paintで140ms前後、Clover SCで90ms前後はあります。そもそもシステムが内部処理で表示しているホバーカーソル(GALAXY Note 10.1ではそういう機能があります)でさえ、70ms以上遅延しているのです。アプリ側がどう頑張ってもそれ以下の遅延になるはずがありません。70msの詳細な内訳は測定のしようがないため推測でしか語れませんが、1、3、4の合計が70ms程度の遅延の原因になっているのは間違いありません。
- ペン入力の遅延
それではまず 1のペン入力の遅延から考えていきます。ペンの座標が送られて来る頻度(時間分解能・スキャンレート)は現状ほとんどのハードで最高7ms~17ms間隔になっています。画面リフレッシュ間隔で言い換えると、これは130fps~60fps程度の頻度速度ということになります。スタイラスから取得したデータが一切遅延なくプログラムに伝えられたとしても、最高で座標が送られてくる間隔分、つまり7ms~17msの遅延が発生するわけです。
私は特にこの辺りに詳しいわけではないので正確なことはわかりませんが、いったん取得された座標データそのものは特に大きな遅延はなく(USBの速度は十分、遅延も大きくはないし、バッファリングする理由も特にない)プログラムに伝えられるのではないかと考えています。ただし、最低でも平均で時間分解能(スキャンレート)の半分の遅延が発生するわけで、時間分解能が高ければ高いほど、遅延が少なくなる第一の理由がこれになります。もし時間分解能が2msになれば、この段階で発生する最大遅延も単純に2ms、平均遅延は1msになるわけです。
- 画面演算による遅延
2のストローク演算処理による遅延は、アプリケーション処理内容に起因するもので端末やOSの特性による回避不能な遅延とは別なので後回しにして、次に3の画面演算による遅延を考えます。
昨今のハードウェアと、ファミコン時代のハードウェア、どちらが遅延が少ないと思われますか?と聞かれれば大体想像がつくでしょうが、実はファミコン時代の方が圧倒的に遅延が少ないのです。いまのCPUやGPUの演算速度はその頃に比べ数百万倍にもなろうというのに、どうしてそんなことになるのでしょうか。
それは画面表示のためのデータの扱いの違いに原因があります。
ファミコン・スーパーファミコン世代のコンピューターは扱えるメモリが少なく、低解像度でも1画面のデータをそのままメモリに入れることはできませんでした。そのためどうしたかというと、8x8等の小さなパターンをいくつか作っておき、8x8パターンのどれを表示するかという番号を並べて背景画像を作っていたのです。(今で言うアスキーアートのようなものと考えれば大体合っています。AAに加え、個々の文字にある程度自由に色・形状を設定できるようにしただけです)ただし、それだけだと8ドット単位でしか移動できないし、背景とキャラクターを重ねることもできません。そこでスプライトという特別な表示機能を用意し、それを使うことでシューティングの自機等、1ドット単位で自由に動くキャラクターを表示していたわけです。ただし、この頃のGPUは貧弱なためスプライトを幾つでも処理できたわけではありません。走査線1ラインごとに表示できる数等が制限されていました。というのも、この頃のGPUはラインバッファといって、1ライン分だけのバッファをその場で構成し、その都度ブラウン管TVに送っていました。この頃のアナログTVは約60fpsで200本の走査線を送り出していましたから、単純に考えて1200fps以上でVRAMバッファ書き換えを行なっていたわけです。すごいもんです。60fps間隔で上から下へ200本の走査線を表示しますが、下から上へ走査線が移動するときに、垂直ブランク期間と言って少しだけ走査線表示が途切れる期間があります。この間に、次の画面用に背景パターンの数値を入れ替えたり、背景のドット単位のズラしレジスタを調整したりすることで、スーパーマリオのようなスクロールをさせたり、キャラクターを移動させたりしたわけです。さらには200本の走査線毎に、右の末端から左へ戻る、ほんの一瞬の表示の間隙(これを水平ブランク期間と言います)をぬってそのタイミングで背景をズラしたりレジスタをいじったりすることで、ラスタースクロールなどという特殊なエフェクトを行うソフトもありました。これには有名なところではドラゴンクエストの「たびのとびら」エフェクトがあります。スケール等も同時にいじって擬似的に奥行きがあるような背景効果を作っていたアーケードゲームも多々有りました。今では完全に忘れ去られてしまった技術ですが、なかなか面白いもんでした。
なんだか懐古趣味に走って話が少々横にそれてしまいましたが、この時代のハードウェアはラインバッファであるため、さっき言った水平ブランク期間等で書き換えを行った場合、ほぼ遅延ゼロでプログラムが書き換えた情報が画面に反映されていました。・・・まあこれを何も考えずに行うと今で言うティアリングが発生するため、大抵の場合は垂直ブランクまで情報をためて、そこで一気に切り替えという方法を採っていたはずですが、それでもほぼ1フレーム程度の遅延で入力された情報が画面に反映されていたわけです。この時代のハードウェアは。
一方で、最近のハードウェアはどうでしょうか。最近のハードウェアはすべてフレームバッファタイプのものになっています。フレームバッファとは、1画面分のバッファをまるまるメモリに貯めこんでおくタイプのハードウェアを指します。ちなみにラインバッファがフレームバッファに切り替わったのは大体、初代プレイステーションの頃です。3DS/PSはフレームバッファ、SSはフレームバッファとラインバッファの間の子みたいなヘンテコなハード構成になっていました。
フレームバッファの利点は、ラインバッファと違って1200fpsで書き換えないと処理が破綻する!などという事がない、ということです。すべてのデータがメモリ(VRAM)内にあるわけですから、たとえ処理が間に合わなくても最低限、前のフレームのデータはTVに表示されるわけです。
ところで、この頃のハードウェアに垂直ブランク期間に全画面分のメモリ内容を書きかえる程のパワーはありません。そうするとTVがVRAMのデータを読み込んでいる最中にVRAMデータを書きかえる事になるわけですが、人間の性というか、リッチな絵を描けるとなると描きたくなるわけで、そうすると60fps(16.7ms)でも全然間に合いません。そもそも必ず間に合うならラインバッファでもいいわけです。それにこの頃にはすでに3Dポリゴンを表示したいという要求があり、一度画面全体を消して、奥から三角形のポリゴンを表示していくことで3Dのシーンを構成していくという方法が採られていました。そうなるとタイミングによっては(どんなに高速に描いても画面の上の方では)描きかけのシーンが表示されてしまうわけです。それはまずいということで考えだされたのがダブルバッファです。
ダブルバッファとは、画面サイズと同じフレームバッファを2つ用意しておき、一方をバックバッファ・一方をフロントバッファとして利用する方法です。バックバッファはプログラムがいま3Dのシーンを構成中のもので、画面には表示されません。フロントバッファには前回構築された3Dシーンのデータが入っていて、TVはこれを表示します。バックバッファの3Dシーンが構築し終わったら、垂直ブランク期間に合わせてバックバッファとフロントバッファを入れ替えます。こうすることで、書き換え最中のデータが見えないようになります。バックバッファはこのように便利なものですが、書き換えに時間がかかってもフロントバッファは一応そのまま表示されていて画面に破綻が起きないため、これまでほぼ絶対遵守されていた60fpsの書き換えを守るというタガが外れてしまい、低フレームレートのゲームが溢れかえりました。人間の目はだいたい50fps前後の時間分解能を持っていて、それ以上の頻度で書き換わっているものは連続してアニメーションしているように見えますが、それ以下になると動いたり止まったりしながらアニメーションしているように感じられます。これが60fpsのゲームと、それ以下のゲームが全く違うように感じる原因です。
・・・また話が横道にそれたようですが、Androidの場合、OpenGLやSurfaceViewなどといったゲーム用に用意された高速な描画処理を行おうとすると、どうしてもこのダブルバッファを使わざるを得なくなります。ダブルバッファにすると全画面を毎回書きかえる必要があり(正確には必須ではありませんがほぼそうなります)垂直ブランク同期タイミングに合わせて切り替えることになるため、どうしても1フレーム(16ms)程度以上の遅延がここで発生します。Clover SCでは遅延を回避するために敢えて低速なViewを使ってストローク描画を行なっています。(しかし、正直な所低遅延化に貢献しているかどうかは判然としません)さらにAndroid4.1以降の場合はトリプルバッファと言って、ダブルバッファよりもう一段バッファを深くしてスクロールを滑らかに見せるという処理が加えられるようになりました。一見滑らかな、ぬるぬるした動作にはなるかもしれませんが、遅延という観点からすれば、さらに1フレーム分遅延するようになっただけです。
(6月10日追記)
私の手元にある端末は現状すべてAndroid 4.0.x以下のため試せなかったのですが、オフ会でGALAXY Note 8.0を触らせてもらってAndroid 4.1から搭載された「時間分解能(スキャンレート)を出来る限り画面の更新速度の60fpsと同じに保つ」という処理の内容がだいたい判明したのでその情報を追記します。
結論から言えば期待していたものとは違いました。内部で高レートの座標スキャンを行い、出力だけは16.7msをできるだけ維持するようにしているのならば(本当はもっと高スキャンレートで送って欲しいのですが、それを通常にしてしまうと、アプリによってはおかしくなるものがあるのも事実なので、高スキャンレートを考慮したアプリを起動する時だけターボモードみたいなものになるよう、ユーザーが指定できるようにするのが理想です。ついでにGALAXY Noteのジェスチャも特定アプリでは無効にするようユーザーが指定出来ればいうことなしなんですが)よかったのですが、8msや24/32msなどといった16msからかけ離れたデータが10.1と同じくらいの頻度であらわれます。多数のデータの平均を取ると確かに16.7msピッタリくらいになるのですが、これでは安定しているとはとても言えません。GALAXY Note 8.0だけの話なので、Android 4.1のすべてがそうなのかはわかりませんが、こんなもののせいで4.1では平均スキャンレートが60ppsで頭打ちになるのであれば、そんな機能はいりません。これでは単なる足枷です。
(追記 ここまで)
(7月17日追記)
その後調べていてわかったのですが、Androidは座標データを送ってくるイベント間隔こそ60ppsなものの、そのイベントの中に以前のイベント以降に検出した座標データをすべて取得する特殊なインターフェイスを備えていて、それを使えば全スキャンデータを取りこぼしなく取得することができることが判明しました。(AT703で200pps、それ以外は133pps)遅延を考えるとハードウェアからデータが来た瞬間に送ってくれた方がいいのですが、単純にそれをやると前述のようにそれを考慮していないアプリは動作がおかしくなるかもしれないし、画面更新タイミングも60fps間隔なのでそれ以上高速に書き換えても無駄なため、この構造はある程度合理的です。問題があるとすれば、ハードからデータが来た瞬間に送ってくれていれば、その瞬間から計算を始められるので、計算に関わる遅延が多少削減できるかもしれない・・・といった程度でしょうか。
(追記 ここまで)
さらにAndroidではアプリケーションのレンダリング内容をOS側が合成したりするための処理段階があるように感じられます。恐らくこの部分でも1フレームか2フレーム程度の遅延が起きています。勘弁して欲しいものです。ただしこれはAndroidだけの話ではなく、最近ではWindowsでも、Aeroでアプリケーションの表示内容をタスクバー等に縮小表示する機能やウインドウの半透明重ね合わせを実現するなどといった非常につまらない事のために同じような遅延が発生しているようです。Windows7まではクラシック表示にすることで回避できたようですが、Windows8からはそのようなオプションもなくなり遅延は回避不可能になっています。遅延など誰も気にしないと思っているんでしょうか。
ユーザーが声を上げなければ、この部分の遅延はどんどん増える一方です・・・
- 画面(モニター)表示の遅延
これは比較的有名な話で、誰でも感じたことがあるだろう、液晶の遅延です。残像として見えることが多いものです。
昔のブラウン管モニターは蛍光面に電子ビームを当てて発光させるタイプのものなので、光っているのは1フレーム以内、遅延も1フレーム以内になります。
それに比べ現在主流の液晶は、バックライトは常時発光していて、バックライトの光をどの程度通すかを液晶に電気信号を送ることで変化させて制御しているわけですが、液晶の種類によっては変化完了までに数十秒以上かかるものがあります。
最近ではひどい残像を感じるような液晶は少なくなって来ましたが、液晶を素早く変化させるために、前のフレームとの差分を取って電気信号を適切な値に調整する回路が入ったりして、残像こそ感じないものの遅延は増えている、などというものも存在します。モニター側で色補正・動画補正・その他フィルタ機能などが入っている場合も、その処理のために遅延が発生していることが多いです。Androidタブレットの場合、搭載されている液晶のこういった性能が表記されていることはまずないので、これもユーザーからはなかなかわからない部分ですが。
- 端末・OSに起因する遅延まとめ
以上、端末・OSに起因する3つの遅延「ペン入力」「画面演算」「モニター」について比較的突っ込んで見て来ました。私はソフトウェアが専門ですがゲームプログラマなので、ハードに関する知識が多少必要だったため、遅延の原因に関しても多少は目星がつくわけですが、逆に言えばゲームプログラムに必要な程度の知識しかありません。もし現状に即しておらず、間違っている部分や、さらに深くご存知の方があれば、コメント等でご教授頂ければ幸いです。
現在のデジタイザ付きAndroidタブレットでは(GALAXY Note10.1/ThinkPad Tablet/HTC Flyerで確認)端末・OSに起因する遅延が、70ms~100ms程度発生しています。
私の推測では「ペン入力」部分で1フレーム程度(平均で8ms)、「画面演算」部分で2フレーム程度(32ms)、「モニター」部分で2フレーム程度(32ms)発生しているものと考えています。合計すると70ms前後です。端末メーカーやOSのチューニングを低遅延に向けて頑張れば、すぐに30ms程度は削減できるはずです。これはおよそストローク予測2フレーム分(予測については後述)にあたります。
ペイントアプリの遅延について
ここでこれまで説明したような大きな遅延がAndroid端末では現状不可避についてまわること、私が絵の訓練をまともにしていないため、スムーズな画面の回転機能がどうしても欲しかったこと、またCloverメモ開発当初は本格的なペイントアプリを作ることは予定しておらず、むしろゲームエンジンを作るためのハードの下調べのために、ちょっとした軽いアプリを作った方が目標になっていいという程度の認識であり、ペイント版もその流れで作られていること、などから、Clover Paintは遅延に関してかなり無頓着な作りになっています。具体的にはClover Paintではアプリケーション起因のペンストローク遅延が70ms程度発生しています。これをClover SCでは10ms前後程度まで短縮しているのですが、その変更点の詳細をここで説明します。4月24日の以下のツイートで遅延にこだわるならばやるべきだと言っている事を自分自身やったにすぎませんが。
まあなんにせよ、細いペン一種類だけのブラシなんだし、その部分にこだわるなら、最初は予測で多少不正確なラインを描画しておき、ある程度ストロークが進んだら遅延して線を修正するという方法を採るべきです
— Clover Paint (@Clover_Paint) April 23, 2013
(ただしここで言っている予測とは多少不正確な線を描くという意味で、後で説明するストローク予測とは内容が異なります。ややこしいですが)
この前後のツイートでも話していますが、8ms~16ms程度の間隔で座標が送られてくるということは、例えば1秒で10センチペンを移動させると、10cm*16ms/1000msで、0.16cmとなり、0.8mm~1.6mm程度の間隔で座標が来るということになります。この間隔ならば補間がなくても、まあそれほど気にはならないのですが、長い髪の毛などを綺麗に描くため素早くペンを動かすと、これが1cm程度になります。ここまでくると少しのカーブでも補間がないとガクガクになってしまいます。
これはClover Paintで補間のON/OFFスイッチを切り替えて同じように描いたものです。違いは歴然です。Clover Paintの補間は現状、この種の補間で、もっとも扱いが簡単だと思われる二次Bスプラインで行なっています。これがどういうものは以下の図でイメージしてください。
意味がないので数式等は省きますが、これでだいたいのイメージは掴めると思います。「1」「2」などとなっているのがスタイラスから送られてきた座標です。4つ目の座標が来るまで、描画を開始できないのがわかります。
一見すると、3つ目のポイントが来た時点で、曲線を構成するための情報は揃っているので描画できるじゃないかとおもわれるかもしれませんが、3つめのポイントでストロークが終わり、4つめのポイントが来ないことがあり得ます。その場合、1-2-3を使った、3の終点までのカーブを描く必要があるため、4で描いた曲線とは異なるものを描かなければならないのです。というわけで、4が来るまでは描画できません。その後はポイントが来るたびに各座標の中点を結んだ曲線を描くことが出来るようになります。ポイントの来る間隔が1フレーム(16ms)だとすると、1のポイントが来た時間を起点として、4のポイントが来るまでの時間は3*16ms=48msになります。5以降は2.5ポイント前まで描画されているので2.5*16ms=40msと考えていいでしょう。ただし、実際のClover Paintではストロークの抜き処理を実現するため、実は図よりもさらに1フレーム待っています。つまりポイントの来る間隔が1フレーム(16ms)だとすると、56ms~64ms程度の遅延が発生します。実際には私の使っている端末は平均13ms程度のなので、この部分の遅延はもう少し小さくなります。さらにClover PaintではOpenGL描画を採用しているため、ポイントが来てからストロークを描画し、それを元にテクスチャを構成、そのテクスチャをバックバッファに描画、さらにそれをフリップしてフロントバッファで表示という手順を踏む必要があるため、ここで最低1フレーム以上の遅延が発生します。
以上の合計でだいたい4~5フレーム程度ということになり、ポイントの来る間隔によりかなり変動はあるものの、Clover Paintのアプリに起因する遅延は60ms~80m程度ということになります。
以上の遅延要因を見れば分かる通り、実はCPUの速度により削減できる遅延は、上記の「ポイントが来てからストロークを描画」の部分だけであることがわかります。非常に太いブラシや複雑な混合処理を持ったブラシの場合、ここにものすごい時間がかかることもあるのですが、Clover SCで使っているような数ピクセル幅の単純なブラシでは、この辺りの処理は2ms以下程度で終わります。そしてそれは大抵、垂直同期でのフリップ待ちなどといったディレイ要因で吸収されてしまいます。つまりCPUがいくら速くても全体の描画遅延には特に影響しないと言えます。
また、現状の端末のポイントの来る時間間隔では非常に大きな遅延になっているこの部分の処理ですが、「ポイント間隔(ms)x4+16ms」という式で表せることに注意してください。この式に当てはめれば、ポイントの来る時間間隔が16ms→2ms等にさえなれば、それだけで80msあった遅延が、24msまで短縮されるのです。私がClover SCのようなツールを作り、ポイントの送られてくる時間間隔(時間分解能・スキャンレート)の計測にこだわる理由の一部がこれで分かって頂けたでしょうか。
- Clover SCのペンストローク
Clover SCでは上記のような遅延を少なくするため、ストローク途中で以前のキャンバス状態を(Undo用とは別に)保存しておき、必要に応じて以前のストローク描画を書き換えながらペンストローク描画を進行させます。以下の図を前のClover Paintの図と比べて下さい。
青が、とりあえず確定はしていないけど、やり直し可能な状態を維持して描画している部分です。赤は描画内容が確定した部分、黄は前ステージで青く描画されていたものを、消した部分です。赤と青がそこで実際に表示されているものになります。見て分かる通り、本来ならば4が来るまで表示開始されていなかったものが、2の時点で表示されています。これで2フレーム分の遅延短縮になります。実際にはCloverPaintではさらに1フレーム待っていますので、3フレームの短縮になります。4から5の黄→青の部分の変化を見ると、かなり変な表示になっているように思いますが、実際にはほとんど気にならないようです。
ただし、以前の描画を消す処理と、新しく描画する内容が増えているため、CPUのストローク処理内容は数倍に増えています。細いブラシならばこの程度の差は誤差範囲に収まりますが、太いブラシではかえって遅延を大きくする要因になる可能性があります。
また、Clover SCではOpenGLによる描画をやめることで、ダブルバッファのフリップ待ちの遅延を回避しています。実測するとスタイラスの座標イベントが来てから5~10ms程度でシステムの描画処理へデータが渡っています。Clover SCの理想的な動作環境でのアプリ起因による遅延は「ポイント間隔x0.5+5~10ms」で、だいたい10ms~18ms程度だと考えられます。
- Clover SCのペンストローク予測
Clover SCではこれにとどまらず、現在までのストローク情報から、この先到達するであろうストローク位置を予測し、あらかじめそれを描画しておくことで、本来アプリ側では解消しようのない遅延を見た目少なくする機能を実験的に実装しています。これは私自身、以下のツイートについて、確かに気持ち悪くなるだけでしょうねと言ったものの、一応確認のために実装してみたものです。
@abfly 予測する方法はうまくいかないでしょうね。いまのデジタイザの時間分解能では速い筆致だとペン座標の間隔が1cmを超えますし、それを元に予測すると、折り返し部分などでかなり変な表示になって逆に気持ち悪くなるだけかと。
— Clover Paint (@Clover_Paint) April 30, 2013
Clover SCを動かして、実際に試してみてもらえばよくわかりますが、予想通り折り返しなどで変な表示になって気持ち悪いのは確かなんですが、絵描き用途だとそこまで複雑に折り返すということは滅多にないため、遅延の少ない描画に感じられ、気持ちいいことも結構あります。文字とかを素早く書くと全然ダメですが。
上の図で緑になっている部分が予測によるストロークの描画です。
予測は少々ややこしい事をしているので詳細は省きますが、Clover SCでは過去のポイント間隔を1フレームとして「1~3フレーム先」までの予測を選んで表示することができます。HTC Flyerでは16ms間隔で安定しているため、かなり綺麗な予測になっていて、3フレーム先予測にするとペン先と描画がほぼぴったりと合って非常に気持のいいストローク描画になります。
まとめ
Android端末等の遅延に関する考察と、Clover SCの低遅延ストローク描画に関する説明は以上です。デジタイザー付きAndroidタブレット端末をお持ちの方は、是非Clover SCで予測ありストロークを試していただきたいのです。そして、それを見て、そのストロークの描き味が変な予測ではなく、普通に得られるようになればなぁという、私の願望に同調してくださる方は、手持ちの端末をClover SCで試し、ペイントアプリの描き味に関わる描画性能をブログやSNS等ネットでどんどん流してください。
もし、Clover SCのペンストロークが気に入り、これを軽いペイントアプリとして使いたいという方があれば、寄付版を用意してありますので、そちらをご購入ください。寄付版を購入し、ペイントアプリとして使いたいという要望が多ければ、機能を追加していくつもりです。ただし、寄付版はCloverSCをAndroidアプリとして考えた場合、かなり高めの値段設定をしています。普通に考えればこんな低機能なアプリを好き好んで買う人は限られているだろうし、アプリ化はこういった活動に意義を感じ、支援してくれる人に対するオマケだと考えているからです。
Clover SCに関しては私自身が好きで作っているわけで、こんな考え方をするのもどうかとは思いますが、大部分はClover Paintの流用とはいえ、開発にフルで一ヶ月強の時間と労力をかけています。経済学の機会費用の考え方でいけば、百万円くらいの費用がかかっていることになります。Clover SCの趣旨からして、有料にすると意味が無いため、その経費はすべて私が一人で負担しています。もし私のやっているようなことに賛同し、自分も時間とプログラム技能さえあればClover SCのようなアプリを開発していたのになぁ・・・という方があれば、経費の分担という意味合いで寄付して頂ければと考えています。いま一部で流行っている、開発費を利益目当ての投資家や銀行から集めるのではなく、そのプロジェクトに意義があると考える、一般消費者や賛同者から集めるというクラウドファンディング的な考え方です。というわけで、前述のペイントアプリ化云々は、賛同してくださった方へのささやかなお礼程度のアプリや機能になる予定です。