跳轉到主要內容

性能

本節將討論嵌入式圖形化使用者介面的性能。

這裡的高性能是指在獲得所需圖形效果和動畫時實現高幀率。

我們回顧一下上一節中關於主迴圈如何影響使用者介面幀率的內容。 再次假設有一台連接到LTDC的平行RGB顯示器和兩個影像緩衝。 基本情況如下:

雙影像緩衝

假設顯示器每秒刷新60次,則每次刷新之間的間隔約16 ms。 計算過程如下:1 s / 60 = 0.01667 s = 16.67 ms。

在1號影像緩衝的傳輸開始時,TouchGFX開始將1號影像繪製到2號影像緩衝。 如果1號影像的渲染在下一次傳輸開始前完成,則可以傳輸2號影像緩衝。 如果沒有在16.67 ms內完成,則再次傳輸1號影像緩衝,並且顯示器的顯示內容不變:

主迴圈時間超過16.67 ms

這種情況意味著丟幀。

採集和更新階段的時間通常是極短的,如少於1 ms,在考慮主迴圈消耗的總時間時,多少有些微不足道。 因此,一般在考慮渲染時間時,其中包括採集和更新階段。

如果許多影像的渲染時間超過16.67 ms的時限,顯示器上的幀率將是30幀每秒(fps)。

如果渲染時間大體上短於16.67 ms,但有一些幀超過16.67 ms,則平均幀率可能接近60 fps,但在用戶看來動畫可能不流暢。 動畫中的某些步驟可能看起來過快而某些又過慢,具體取決於應用。 這並不符合我們的期望。

渲染時間還可以更長。 如果剛好超過33 ms,由於每三次傳輸只有一個新影像就緒,幀率將降至20 fps。

FPS最長渲染時間
6016.67 ms
3033.34 ms
2050.00 ms
1566.67 ms

此表顯示了給定幀率可獲得的最長渲染時間(包括採集和更新階段)。

為了獲得良好的使用者介面性能,最好定期檢查和監測幀率。 可使用兩種方法:

  • 測量渲染時間
  • 丟幀計數

測量渲染時間

測量渲染時間的第一種方法提供了最詳細的資訊。 其理念實際上是測量從影像傳輸到渲染階段結束之間的時間。 圖形引擎在採集階段開始時呼叫GPIO類的函數,並在渲染階段結束時再次呼叫。 應用程式定義這些函數,並能導入函數以便執行測量。

測量可通過兩種方式來執行:

  • 使用外部計時設備,如示波器:為了使用示波器進行測量,應用應使用從GPIO介面去實現set(GPIO_ID)clear(GPIO_ID)的方法。 然後,示波器可以測量輸出為高電位的持續時間,以此作為渲染時間。
  • 使用內部計時器:另一種方法是使用內部計時器,如sysTick計時器。 在呼叫GPIO::set(RENDER_TIME),應用可將計時器的值保存在變數中。 在進行清零呼叫時,應用可再次讀取計時器並減去前值,從而獲得渲染時間。 計時器的速度將決定測量精度。 應用必須以某種方式使渲染時間可見。 一種方式是將值保存在全域變數中,並且可以在介面上的TextArea中顯示值。 也可使用除錯器檢查值。

丟幀計數

圖形引擎對上一個採集-更新-渲染階段中發生的傳輸次數進行計數。 應用可輕鬆地檢查此值,以便瞭解是否丟幀以及幀率是否下降。

計數在HAL類中提供:

void handleTickEvent() {
tickCounter += 1;
if (HAL::getInstance()->getLCDRefreshCount() > 1) {
//Alert programmer somehow
...
}
}

丟幀補償

如果丟幀且某個動畫的幀率因此下降,可進行一定程度的補償。 我們可以:

  • 等待其結束 - 讓動畫繼續,這會導致動畫持續時間變長,並且動畫可能不流暢。
  • 跳過一些幀 - 通過跳幀確保整個動畫的持續時間不會超過預定時間。

如果丟幀,可指示TouchGFX自動跳過一些幀。 這可以通過在每個實際幀中多次勾選動畫來實現。 當渲染時間不穩定時,這有助於讓動畫更流暢。

HAL.hpp
void setFrameRateCompensation(bool enabled)

影響渲染時間的因素有哪些?

影響渲染時間的因素有許多:更新部分的大小、分層的使用、小工具的複雜度和可使用於渲染的硬體支援。

介面更新了多少?

渲染時間通常與必須更新的像素數成正比。 如果動畫需要的渲染時間過長,一種可能的解決辦法是縮小動畫面積。 例如,如果有一張旋轉圖像而性能不夠好,則可通過縮小圖像尺寸來改善性能。

通過縮小圖像尺寸縮短渲染時間

圖形引擎會重繪應用中無效的區域。 這意味著必須只使實際需要刷新的區域無效化。

無效區域越大,渲染時間越長。

圖形中的層數

在典型應用中,圖形將包含彼此堆疊的不同元素。 如果更新了元素中的一個,通常必須重繪所有元素。

典型的例子是背景圖像、幀和一些文字:

分層圖形元素

此使用者介面的創建方法是將TextArea小工具放在顯示了一個透明影像的Image小工具上方。 二者都在背景圖像上方:

TouchGFX Designer中的分層圖形元素

此解決方案在應用中經常用到。 這是一種十分簡單的解決方案,具有高度的靈活性,例如,可以在執行時間更改影像或在背景上移動影像和文字。

關於渲染時間的問題是如果在執行時間更新了文字且需要重繪,圖形引擎還需要重繪背景和影像,然後是新的文字。 這會顯著增加文字的渲染時間。

無效區域的層數越多,渲染時間就越長。

渲染像素的複雜度

將每個像素渲染到影像緩衝區的難度並不一致。 在所有類型的渲染中,圖形引擎必須將所得像素寫入影像緩衝。 但是,要寫入的像素的計算成本並不相同。

固定色彩(如Box Widget中使用的色彩)的成本最低,只需計算一個像素並將結果重複用於所有像素。 這意味著使用許多Box可獲得非常高的性能。 由於這會導致使用者介面品質不高,因此不建議這樣做。

第二低的是圖像的像素計算成本,這是因為像素均以可直接使用的格式儲存在點陣圖中。 計算要寫入影像緩衝的像素關係到從點陣圖中的正確位置載入色彩值。

文字的成本與圖像相當,每個字母實際上都是一幅小圖像。 事實上,由於大量小圖像導致了相當高的“開始-停止”成本,因此文字的成本更高。 例如,每個字母的位置計算。 為了讓文字看起來盡可能美觀,會將文字顯示為具有透明度的小圖像,請參見下文關於透明的注釋。

旋轉或縮放後的圖像成本更高。 任務同樣是從點陣圖載入像素值,但由於圖形引擎必須包含縮放和旋轉,因此這時的計算更耗時。

幾何元素(如圓形)的成本更高。 這時我們不能從點陣圖載入像素色彩,而是必須計算圓的形狀和圓中每個像素的色彩。

透明度增加了元素的繪製成本。 如果一些像素不是實心的,那麼元素是透明的。 圖形引擎首先必須繪製透明元素後方的元素(如“影像中的文字”章節所述),這會增加繪製成本。 其次,圖形引擎隨後必須將背景像素與透明元素的像素進行組合,並將結果寫入影像緩衝。 此類計算的耗時顯著多於只寫入計算的像素。

Box、Image、旋轉Image和圓。 實心元素位於第一行。 透明元素在下方。

透明總是會多出一層。 但是,將實心像素放在其他實心像素的上方並不一定會增加層數。 圖形引擎儘量不繪製被其他實心圖元覆蓋的像素,因為這是在浪費寶貴的時間。

無效區域中元素的成本越高,渲染時間就越長。

記住,只有無效區域中的元素才會增加渲染時間。 無效區域之外的元素對渲染時間無影響。

點擊此處閱讀關於UI元件和性能的更多內容。

渲染的硬體支援

一些STM32微控制器包含圖形加速器Chrom-ART(或DMA2D)。 此加速器可縮短渲染時間。 由於加速器與微控制器核心平行運行,微控制器可以在加速器渲染圖形時自由地運行其他任務。

Chrom-ART主要用於圖像和文字。 當啟用時圖形引擎會自動使用它。

何時應考慮渲染時間

渲染時間並非總是那麼重要。 當低幀率可被用戶觀察到時,應注意渲染時間。 當動畫在部分介面上運行(如旋轉的圖示)或您在介面上移動或滑動某元素時,通常就屬於這種情況。 如果更新頻率低,那麼在使用者看來,動畫將呈現出分步顯示而非流暢的狀態。 如果是這樣,應檢查渲染時間。

另一方面,如果用新介面替代整個介面,當更換期間幀率顯著下降時,用戶通常注意不到。 這是因為用戶看不到渲染何時開始,只能看到它何時結束。

這兩條規則意味著對於動畫元素(如移動元素)而言,應使用較少的層數,避免使用複雜元素和許多層數。 對於介面的其餘部分,這些不是問題。

類比時鐘和滾動清單

在本例中,左側有一個類比時鐘。 通過旋轉三幅細長的圖像渲染三根指針。 這通常不難實現,因為指針並非總是在移動。 但如果我們要讓時鐘在介面上到處移動,將會在每一幀中重繪指針,由於繪製旋轉圖像通常比較耗時,因此會比較麻煩。

右側是一個滾動列表。 使用者可以上下移動此數字清單,為了讓使用者介面顯示出高回應性,需要高幀率。 因此,必須考慮滾動清單中元素的渲染時間,或者縮小滾動列表的尺寸。

通過使內容無效來優化性能

通常整個小工具均無效,但圖形引擎只能使小工具的內容無效,而不能使整個小工具無效。 通過減少無效區域,渲染時間一般會明顯縮短。 渲染時間的改進取決於:

  • 小工具內容覆蓋的區域相對於整個小工具的大小。
  • 背景小工具部分或全部被小工具覆蓋。

下圖以TextArea小工具為範例說明了內容無效的概念。 圖1顯示了小工具的整個區域。 圖2顯示了使用TextArea::invalidate()時的無效區域。 圖3顯示了使用TextArea::invalidate()時的無效區域。

圖1. 橫跨整個螢幕寬度的文字區域

圖2. 使用TextArea::invalidate()時無效的區域(紅色)

圖3. 使用TextArea::invalidateContent()時無效的區域(綠色)

使用TextArea::invalidateContent()的範例

在小工具與其他小工具重疊的情況下,使用TextArea::invalidate()使整個TextArea無效時,需要重新繪製其他小工具。 通過改用TextArea::invalidateContent(),我們將不必要的無效和重繪小工具的風險降至最低。 這對於昂貴的小工具尤其如此,例如:圓、儀錶等。

下圖說明如何避免使用TextArea::invalidateContent()使背景小工具(圖像-意法半導體標誌)無效。 如果我們使用TextArea::invalidate(),將會使後臺控制項無效並重新繪製。

使用TextArea::invalidateContent()的範例

獲得良好性能的建議

我們總結了獲得良好性能的建議,以結束本節內容:

  • 不要重繪未更改的部分 確保不會意外地讓介面上不必要的部分失效。 這會降低性能且無任何益處。
  • 在品質與速度之間尋求平衡 降低元素的複雜度有助於提高性能。 複雜度與性能之間的良好平衡通常極為關鍵。
  • 利用硬體能力 具有硬體加速(Chrom-ART)的微控制器的能力通常高於沒有硬體加速的微控制器。 考慮使用具有Chrom-ART的微控制器。
  • 用圖像替代計算圖形 計算所得到的圓會比圓圖像慢。 一般而言,圖像可替代許多靜態元素。
  • 調整顯示器刷新率 如本節開頭所述,刷新率是渲染時間的硬性限制。 如果渲染時間超過刷新率,幀率將下降。 如果渲染時間只超過刷新率一點點,也許能夠將顯示器的刷新率降至如55 Hz(相當於18.2 ms)這樣的水準,並維持高幀率。