キャッシュ可能コンテナによるパフォーマンス向上
このセクションでは、いくつかのアニメーション・シナリオにおいて、RAMを使用して一部の再利用可能な描画を保存することで、パフォーマンス向上を達成する方法を説明します。
ドラッグまたはアニメーションによって、アプリケーション内でウィジェット(画像やテキスト・エリアなど)を移動する場合、TouchGFXではすべてのフレームの新しい位置にこれらのウィジェットを再描画する必要があり、それまでのウィジェットがカバーしていたバックグラウンド部分であっても再描画する必要があります。
これらのウィジェットが、テクスチャ・マッパー・ウィジェット、シェープ、または大きな透過画像など、複雑な計算を要するものである場合、マイクロコントローラが効率的にレンダリングするのは困難です。これらはハードウェア・アクセラレーションを使用せずにレンダリングされるからです。 この結果、スクリーンの再描画に何ミリ秒もかかるようになり、アプリケーションのパフォーマンスにも影響します。
ここでは、キャッシュ可能コンテナを使用して、時間のかかる再描画を避けることで、計算が複雑な要素を含むアニメーションの速度を上げる方法について取り上げます。 この記事での測定はSTM32F429Discoveryボードを使用して行われましたが、一般的にキャッシュ可能コンテナによる手法は、その他のハードウェア・プラットフォームに適用されます。 提供されているRAMの一部は、ビットマップ・キャッシュの作成のため必要になります。
Further reading
パフォーマンスへの影響
マイクロコントローラでの計算に時間のかかるウィジェットの移動はパフォーマンスに影響を及ぼすので、多くの小さいステップを展開するアニメーションは低速かつ鈍い動作で表示されます。フレームごとのレンダリングに時間がかかるからです。 高速で(時間内に)完了するようにアニメーションをプログラミングすると、個々のステップが大きくなり、アニメーションはスムーズに表示されなくなります。
次に示すのはSTM32F429-DISCOボード(240x320)の実行例で、フルスクリーンのコンテナが垂直に上向きに移動すると同時に、同様のコンテナが下から移動してきます。
下のビデオでは、ToggleButtonスイッチで、キャッシュ可能コンテナのオン/オフを切り替えています。 パフォーマンスの違いがはっきりと見られます。
移動する2つのコンテナはそれぞれ、バックグラウンドのボックス、テキスト・エリア、テクスチャ・マッパーで構成されます。 テクスチャ・マッパーは、双線形レンダリング・アルゴリズムとグローバル・アルファ174を使用するように設定されており、描画に非常に時間がかかります。 スクリーン全体のレンダリングにかかる時間は、STM32F429-DISCOボード上でおよそ100msです。
テスト・アプリケーション
相互に関連する2つの要素を移動させるためには、masterContainer
という名前の親コンテナ内にこれらを配置します。この親コンテナはいずれかの子コンテナの2倍の高さに指定するので、ここでは240x640(2*320)
の高さに設定されます。 TouchGFX Designerでコンテナを移動アニメータとして宣言することで、このコンテナはアプリケーション・ティックを受け取り、パフォーマンス測定中に時間の経過とともにアニメーションを実行できるようになります。
container1
という名前の上方のコンテナは、x=0, y=0に配置されています。 container2
という名前の下方のコンテナは、親のmasterContainer
内でcontainer1の真下の、x=0, y=320に配置されています。
container1
とcontainer2
はmasterContainer
内に配置されているので、masterContainer
を移動すると、この2つの要素も一緒に移動します。 たとえば、masterContainer
をx=0, y=-320の位置に移動すると、container1
は表示されなくなりますが、container2
は完全に表示されます。 これら2つの状態間のアニメーションは、TouchGFX Designerのインタラクションを使用して作成できます。
下のコードでは、masterContainer
を、下にある場合は上に、すでに上にある場合は下に移動します。 分かりやすくするために、このコードはビューのhandleClickEvent
イベントハンドラに挿入されているので、ユーザがスクリーン上の(ToggleButtonの下の)どこかをタッチすると常に実行されます。
Screen1View.cpp
void Screen1View::handleClickEvent(const ClickEvent& evt)
{
//Forward event to base View (for the ToggleButton to work)
View::handleClickEvent(evt);
//If touch is released and y > 50 (below the ToggleButton), move masterContainer
if (evt.getType() == ClickEvent::RELEASED && evt.getY() > 50)
{
const int endPosition = masterContainer.getY() >= 0 ? -320 : 0;
masterContainer.startMoveAnimation(masterContainer.getX(), endPosition,
20 /* ticks */,
EasingEquations::cubicEaseInOut,
EasingEquations::cubicEaseInOut);
}
}
複雑なコンテナの再描画のパフォーマンス
前に述べたように、アニメーションの小さな各ステップごとにマイクロコントローラが時間がかかるテクスチャ・マッパーを再描画する必要がある場合、1フレームのレンダリング時間はおよそ100msになります。 つまり、毎秒10フレーム(10fps)です。 アニメーション全体で20フレームなので、およそ2秒かかるということです。
STM32F429-DISCO評価キット上で、レンダリング時間はGPIO G14のデジタル信号として得られます。 VSYNC信号はG13で得られます。 GPIOの設定はGPIO.cpp
ファイルで行われます。
次の画像は、masterContainer
を上向きに移動させたときの、アプリケーションのVSYNCとRENDER_TIMEの測定を示しています。
レンダリング時間は最初の信号(アクティブ・ロー)です。 移動アニメーションの最初のフレームのレンダリング時間は、99.29msであることが確認できます。
下の信号はVSYNCで、ピクセルがディスプレイにクロック出力されるときに、各フレームでハイ(高)からロー(低)に遷移します。 上の測定では、1フレームの描画時間がディスプレイ上の7フレーム分の時間に相当していることがわかります。 8番目のVSYNC信号で、次のフレームのレンダリングが開始されます。 レンダリング中、ディスプレイには(別のフレームバッファ内の)それまでに描画されたフレームが繰り返し表示されます。
キャッシュによるパフォーマンスの向上
上に示した移動アニメーションは、コンテナのレンダリングをメモリにキャッシュすることで、パフォーマンスを向上させることができます。 キャッシュした後は、そのメモリ内に配置されているピクセルを(DMAを使用して)単純にフレームバッファに移動します。マイクロコントローラを使用して複雑なウィジェットを再描画する必要はありません。 マイクロコントローラのみの使用で、アプリケーションが毎秒60フレーム(60fps)を達成可能であっても、同じ計算を繰り返し行うことでマイクロコントローラがビジー状態(おそらく負荷100%)になる可能性は高く、より重要なタスクを実行できなくなります。
コンテナのこの「インメモリ・イメージ」を、スクリーン上のさまざまな場所に表示できます。コンテナを再描画する必要はありません。
最初に行うのは、TouchGFX Designerでキャッシュを有効にすることです。Cacheableプロパティを、container1
とcontainer2
の2つのコンテナ上で有効にします。
次の手順は、これらのコンテナのキャッシュ先として、RAM内に2つのダイナミック・ビットマップを作成することです。
ビットマップ・キャッシュを配置するRAM内のアドレスを割り当てます。 ここに挙げる特定の例では、SDRAM(STM32F429ではアドレス0xd0000000から始まる)をフレームバッファの直後に配置しています。
Windowsシミュレータでは、キャッシュはグローバル変数内に割り当てられます。
Screen1View.hpp
#ifdef SIMULATOR
uint32_t sdramBuffer[8*1024*1024/4];
uint16_t* sdram = (uint16_t*)sdramBuffer;
#else
uint16_t* sdram = (uint16_t*)(0xd0000000 + 320*240*2*2);
#endif
ビットマップ・キャッシュを初期化して、キャッシュ用に2つのダイナミック・ビットマップを作成します。
Screen1View.cpp
//Create bitmap cache and two dynamic bitmap for caching, each bitmap is 150Kb
Bitmap::setCache(sdram, 320*1024, 2); //320Kb cache
dynamicBitmap1 = Bitmap::dynamicBitmapCreate(240, 320, Bitmap::RGB565);
dynamicBitmap2 = Bitmap::dynamicBitmapCreate(240, 320, Bitmap::RGB565);
ダイナミック・ビットマップをコンテナに割り当て、キャッシュ・モードに設定します。
Screen1View.cpp
//Assign the bitmaps to the Cacheable Containers
container1.setCacheBitmap(dynamicBitmap1);
container2.setCacheBitmap(dynamicBitmap2);
//Enable caching
container1.enableCachedMode(true);
container2.enableCachedMode(true);
//Finally update the cached bitmaps
container1.updateCache();
container2.updateCache();
Container::updateCache()
を呼び出すと、2つのコンテナがそれぞれのビットマップ内にレンダリングされます。 コンテナの内容を更新した場合、このメソッドを常に呼び出します。 これは開発者がアプリケーション・コードで処理する必要があります。
container1
とcontainer2
に対してキャッシュを有効にすると、パフォーマンス測定では、レンダリング時間が99msから5msへと20倍の短縮を示すようになります。つまり、60fpsでのレンダリングを実現可能で、20フレームからなるアニメーション全体をより短時間で処理できます。
まとめ
(移動頻度の高い)アニメーションの実行時にダイナミック・ビットマップによるキャッシュ可能コンテナを使用すると、計算が複雑な対象の場合、レンダリング時間を大きく向上させることができます。アニメーションのステップに変更はありません。 キャッシュのコンテンツを更新する必要がある場合(時間更新時の時計の文字盤など)は、アプリケーションによって制御されるアニメーションの実行時の特定のポイントで、キャッシュのコンテンツを再計算することになります。