用可緩存容器實現更優性能
本節介紹如何通過使用RAM來節約一些可重複使用的繪製,從而在一些動畫場景中實現更優的性能。
在應用中通過拖拽或動畫移動小工具(如Image或TextArea)時,TouchGFX需要在每一幀中的新位置重新繪製這些小工具,另外在大多數情況下,還需要重新繪製之前被這些小工具覆蓋的部分背景。
當這些小工具在計算上十分複雜時,如文字映射器小工具、形狀和大尺寸透明圖像,由於沒有硬體加速,MCU將難以高效地進行渲染。 這會導致螢幕重新繪製,將耗費幾毫秒的時間並影響應用性能。
下面我們將討論如何使用可緩存容器,通過避免高成本的重新繪製為包含計算複雜元素的動畫加速。 在使用STM32F429Discovery開發板執行本文中的測量同時,可緩存容器技術廣泛地應用於其他硬體平臺。 為了創建點陣圖緩存,需要一些可用的RAM。
Further reading
性能影響
由於使用MCU移動計算成本高的小工具會影響性能,包含許多小步驟的動畫會因為每一幀的渲染時間過長而顯得緩慢和遲滯。 為了更快結束動畫(在時間上)而對動畫進行程式設計會導致每個步驟的量變大,並且動畫會顯得不流暢。
下面是一個在STM32F429-DISCO開發板(240x320)上運行的範例,其中的滿屏容器垂直向上移動,而一個相似容器從底部移入。
在下面的影片中,ToggleButton在可緩存容器啟用和停用之間切換。 性能差異顯而易見。
移動的兩個容器都包含背景框、 TextArea, 和 文字映射器。 將文字映射器配置為使用雙線性渲染演算法和174的全域阿爾法值,使其繪製成本高昂。 在STM32F429-DISCO開發板上,整個螢幕的渲染時間約為100 ms。
測試應用
為了移動兩個元素同時維持它們的相對位置,將它們放在一個名為masterContainer
的父容器中,父容器的高度是任何一個子容器的兩倍,得到其尺寸為240 x 640 (2*320)
。 通過在TouchGFX Designer中將容器宣告為移動動畫器,它將能夠接收應用標記並隨時間流逝形成動畫,在這段時間裡可以測量性能。
上方容器名為container1
,處於位置 (x=0, y=0)。 下方容器名為container2
,處於位置 (x=0, y=320),在masterContainer
中,它位於container1的正下方。
由於container1
和container2
位於masterContainer
中,在我們移動masterContainer
時,兩個元素會一起移動。 例如,如果我們將masterContainer
移動到位置 (x=0, y=320),container1
將不可見,而container2
將完全可見。 這兩種狀態之間的動畫可使用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);
}
}
重新繪製複雜容器的性能
如前文所述,當MCU必須在動畫的每一個小步驟重新繪製高成本的文字映射器時,一個幀的渲染時間約為100 ms。 相當於每秒10幀(fps)。 整個動畫有20幀,因此需要約兩秒鐘。
在STM32F429-DISCO評估套件上,渲染時間作為GPIO G14上的數位信號來提供。 VSYNC信號在G13上提供。 GPIO配置在GPIO.cpp
檔中設置。
下圖是向上移動masterContainer
時,應用的VSYNC和RENDER_TIME的測量:
渲染時間是第一個信號(低電位有效)。 我們可以看到,移動動畫中第一幀的渲染時間是99.29 ms。
較低的信號是VSYNC,在像素時鐘輸出到顯示器時,它在每一幀從高向低轉換。 在上面的測量中可以看到,繪製單個幀佔用了顯示器上7個幀的時間。 在第8個VSYNC信號開始下一幀的渲染。 在渲染過程中,顯示器重複顯示上一個已繪製的幀(在另一個影像緩衝中)。
通過緩存改善性能
可通過將容器渲染緩存到記憶體來改善以上移動動畫的性能。 此後,我們只需將該記憶體中的像素移動到影像緩衝(使用DMA),而不是使用MCU重新繪製複雜的小工具。 即使應用只使用MCU就可以達到每秒60幀,也會忙於(可能是100% MCU負載)重複執行相同計算,而不是執行更重要的任務。
現在,容器的“in-memory-image”可以顯示在螢幕上的不同位置,無需重新渲染容器。
第一步就是通過TouchGFX Designer使能緩存,勾選容器的 Cacheable 屬性,包括 container1
和 container2
:
下一步是在可以將容器緩存到其中的RAM中創建兩幅動態點陣圖。
決定點陣圖緩存在RAM中的存儲位址。 就本例而言,我們將它放在SDRAM中(STM32F429上從位址0xd0000000開始),就在影像緩衝之後。
對於視窗模擬器,在全域變數中分配緩存:
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
初始化點陣圖緩存並創建兩幅動態點陣圖用於緩存:
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()
會將兩個容器渲染到它們各自的點陣圖中。 在任何需要容器更新狀態的時候呼叫此方法。 這必須由開發者在應用程式碼中進行處理。
在為container1
和container2
啟用緩存後,現在的性能測量顯示,渲染時間從~99ms縮短到~5ms,這意味著可以輕鬆地以60幀每秒的速度進行渲染,並在20幀內完成整個動畫。
結論
如果物件在計算上十分複雜且在不同動畫步驟之間無變化,在製成動畫(頻繁移動)時,使用包含DynamicBitmap的可緩存容器可顯著縮短渲染時間。 如果緩存必須更新(如時間更新時的手錶),在應用控制動畫期間,可以在特定的點重新計算緩存內容。