使用 about:tracing 旗標分析 WebGL 遊戲

莉莉湯普森
Lilli Thompson

如果無法評估,就無法改進。

凱文

您必須先找出效能瓶頸,才能加快 HTML5 遊戲的執行速度,但實際做法並不容易。評估每秒影格數 (FPS) 資料只是第一步,但如果想全面掌握全貌,就必須充分瞭解 Chrome 活動中的細微差異。

about:Tracing 工具可提供深入分析,協助你避免為了提升成效而採用繁瑣的解決方法,但基本上就是需要憑空猜測。如此一來,您就能省下大量時間和精力,更清楚地瞭解 Chrome 處理每個影格的情況,並根據這項資訊將遊戲最佳化。

哈囉:追蹤

Chrome 的「關於:追蹤」工具可讓您瞭解一段時間內 Chrome 的所有活動,而且精細程度相當高,可能導致 Chrome 剛開始時無所適從。Chrome 的許多功能均經過特殊設計,可直接進行追蹤,因此您不必手動檢測,就能使用 about:tracing 來追蹤效能。(詳情請參閱後續章節,瞭解如何手動檢測 JS)

只要在 Chrome 的網址列中輸入「about:tracing」,即可查看追蹤檢視。

Chrome 網址列
在 Chrome 的網址列中輸入「about:tracing」

您可以透過追蹤工具開始錄製、執行遊戲幾秒鐘,然後查看追蹤記錄資料。資料可能的示例如下:

簡易追蹤記錄結果
簡易追蹤記錄結果

沒錯,這點實在很令人困惑。讓我們來談談如何閱讀

每一列代表一個剖析的程序,左軸代表時間,每個彩色方塊都是檢測函式呼叫。不同種類的資源都有顯示資料列。對遊戲剖析作業最有趣的部分是 CrGpuMain,用於說明圖形處理器 (GPU) 和 CrRendererMain 正在執行的作業。每個追蹤記錄在追蹤期間內,每個開啟的分頁都會包含 CrRendererMain 行列 (包括 about:tracing 分頁)。

讀取追蹤記錄資料時,第一項工作是判斷哪些 CrRendererMain 資料列與遊戲相符。

簡易追蹤記錄結果
簡易追蹤記錄結果

在本範例中,兩個候選字詞為:2216 和 6516。不過,目前對於挑選應用程式的方法並不完善,只不過您可以尋找執行大量定期更新的那一行 (或者,如果您手動使用追蹤點檢測程式碼,找出包含追蹤資料的那一行)。在此範例中,6516 似乎正在執行因更新頻率而執行的主要迴圈。在開始追蹤之前關閉所有其他分頁,會更容易找到正確的 CrRendererMain。不過,遊戲以外的處理程序可能還是會有 CrRendererMain 資料列。

尋找外框

在遊戲追蹤工具中找出正確的資料列後,下一步就是找出主要迴圈。主要迴圈就像追蹤資料中的重複模式。使用 W、A、S、D 按鍵可左右移動 (來回移動),還可使用 W 和 S 縮放資料。如果遊戲以 60Hz 執行,主要迴圈會是每 16 毫秒重複一次的模式。

看起來是三個執行影格
看起來像三個執行框架

找出遊戲心跳後,就能深入瞭解程式碼在每個影格中執行的確切動作。使用 W、A、S、D 來放大,直到您讀取功能方塊內的文字為止。

深化至執行影格
深入瞭解執行影格

這個方塊集合顯示一系列的函式呼叫,每個呼叫會以彩色方塊表示。每個函式是由上方的方塊呼叫,因此在這個範例中,您可以看到 MessageLoop::RunTask 名稱為 RenderWidget::OnSwapBuffersComplete,而它又叫做 RenderWidget::DoDeferredUpdate,以此類推。查看這項資料,您可以完整瞭解各項執行作業呼叫的內容和所需時間。

但在這裡還動一點about:tracing 揭露的資訊是來自 Chrome 原始碼的原始函式呼叫。您可以根據名稱猜測每個函式的用途,但這些資訊並不容易理解。查看影格的整體流程固然很好,但您需要較易讀懂的內容,才能瞭解實際情況。

新增追蹤記錄標記

幸好,在程式碼中加入手動檢測,就能建立追蹤記錄資料:console.timeconsole.timeEnd

console.time("update");
update();
console.timeEnd("update");
console.time("render");
update();
console.timeEnd("render");

上述程式碼會在追蹤檢視名稱中,以指定的標記建立新方塊,因此重新執行應用程式時,您會看到「更新」和「顯示」方塊,其中顯示每個代碼開始和結束呼叫之間的經過時間。

已手動新增的標記
手動新增的標記

使用此方法時,您可以建立人類可讀的追蹤資料,以追蹤程式碼中的熱點。

GPU 還是 CPU?

使用硬體加速圖形時,您可以在剖析時詢問一個最重要的問題之一:這個程式碼的 GPU 是否受限?在每個影格中,您必須在 GPU 和 CPU 上進行一些轉譯工作;為了瞭解遊戲速度緩慢的原因,您需要在這兩項資源之間取得平衡。

首先,在名稱為 CrGPUMain 的追蹤記錄檢視畫面中,找到代表 GPU 在特定時間是否忙碌的行。

GPU 和 CPU 追蹤記錄

您可以看到遊戲的每個影格都會導致 CPU 在 CrRendererMain 和 GPU 中運作。上方的追蹤記錄就是一個十分簡單的用途,其中 CPU 和 GPU 在每 16 毫秒影格的多數影格中都處於閒置狀態。

當您的遊戲執行速度緩慢,但不確定自己佔用的資源時,追蹤記錄檢視就能派上用場。瞭解 GPU 和 CPU 線之間的關係是偵錯的關鍵。請採用與先前相同的範例,但在更新迴圈中增加一些額外工作。

console.time("update");
doExtraWork();
update(Math.min(50, now - time));
console.timeEnd("update");

console.time("render");
render();
console.timeEnd("render");

現在您會看到如下所示的追蹤記錄:

GPU 和 CPU 追蹤記錄

這項追蹤記錄能提供什麼資訊?我們可以看見,圖片的影格時間從約 2270 毫秒到 2320 毫秒,這表示每個影格的轉譯時間約為 50 毫秒 (畫面更新率為 20 Hz)。更新方塊旁邊會顯示以彩色方塊代表轉譯功能的彩色方塊,但影格完全是以更新為主。

相較於 CPU 運作情況,在大多數影格中,GPU 都處於閒置狀態。如要最佳化這段程式碼,您可以尋找可在著色器程式碼中完成的作業,並將其移至 GPU,以便充分運用資源。

如果著色器程式碼本身速度緩慢,且 GPU 過度工作,該怎麼辦?如果我們從 CPU 中移除不必要的工作,並改在片段著色器程式碼中新增一些工作,該怎麼辦?以下是不需要昂貴的片段著色器:

#ifdef GL_ES
precision highp float;
#endif
void main(void) {
  for(int i=0; i<9999; i++) {
    gl_FragColor = vec4(1.0, 0, 0, 1.0);
  }
}

使用該著色器的程式碼追蹤記錄為何?

使用緩慢 GPU 程式碼時的 GPU 和 CPU 追蹤記錄
使用緩慢 GPU 程式碼時的 GPU 和 CPU 追蹤記錄

同樣,請記下影格的時間長度這裡的重複模式從 2750 毫秒到 2950 毫秒,持續 200 毫秒 (影格速率約 5Hz)。CrRendererMain 行幾乎完全空白,代表 CPU 在大多數時間處於閒置狀態,而 GPU 超載。這有助於您的著色器太過重。

如果您無法確實掌握導致低影格速率的具體原因,可以觀察 5 Hz 更新並嘗試檢查遊戲程式碼,嘗試最佳化或移除遊戲邏輯。在這種情況下,完全沒有任何問題,因為遊戲迴圈中的邏輯並非消耗時間。實際上,這個追蹤記錄代表每個影格執行更多 CPU 工作基本上是「釋放」,因為 CPU 處於閒置狀態,因此提供更多工作,並不會影響影格花費的時間。

實際範例

我們來看看真實遊戲的追蹤資料是什麼吧。使用開放式網頁技術所開發的遊戲還有一個優點,就是可以讓您查看常用產品的最新情況。如果您想測試剖析工具,可以前往 Chrome 線上應用程式商店挑選自己喜愛的 WebGL 標題,並透過 about:tracing 剖析。這是從絕佳 WebGL 遊戲競速選手取得的追蹤記錄範例。

追蹤真實遊戲
玩遊戲

每個影格似乎需要約 20 毫秒,代表影格速率約為 50 FPS。您可以看到 CPU 和 GPU 之間的工作處於平衡狀態,但 GPU 是需求最高的資源。如果您想查看 WebGL 遊戲的實際範例,歡迎透過 WebGL 技術建構的 Chrome 線上應用程式商店遊戲,看看會發生什麼事吧:

結語

如果您想讓遊戲以 60 Hz 執行,則所有影格都必須符合 16 毫秒的 CPU 和 16 毫秒的 GPU 時間。您同時擁有兩項資源可並行使用,您可以轉換這些資源來提升成效。Chrome 的「關於:追蹤」檢視畫面是一項非常實用的工具,可讓你深入分析程式碼的實際運作情形,協助解決正確的問題,進而縮短開發時間。

後續步驟

除了 GPU 之外,您也可以追蹤 Chrome 執行階段的其他部分。Chrome Canary 是 Chrome Canary 的早期版本,經過特別設計,可追蹤 IO、IndexedDB 和其他多項活動。如要進一步瞭解目前的追蹤事件狀態,請參閱這篇 Chromium 文章

如果您是網頁遊戲開發人員,請務必觀看以下影片。這部影片由 Google 2012 年遊戲開發人員大會團隊所發表的簡報,內容是關於 Chrome 遊戲效能最佳化的: