Adobe Scout 使用參考說明


Adobe Scout 用於優化 Flash 內容,是一款極為強大的工具,因為它能讓您看到 Flash Player 幕后正在發生的事情。但是若明白 Flash Player 為什么做這些事情,您看到這些事情才會最有用。只有這樣,您才能有效地找到修復 Scout 所告知的問題的方法!

本文的目的是讓您大致了解 Flash Player 的工作原理,並將之與您在 Scout 中看到的信息相關聯。本文也是 Scout 中所用術語的參考指南,因此您可以從中查找 Flash Player 執行的各種活動的含義。如果您使用過 Scout,並且認為“我花了太多的時間做某件事,卻不知道某件事的含義”,那么本文就是為您准備的!

注意,本文不是 Scout 用戶界面指南。不知道如何使用 Scout,或者不知道各個面板的作用,那么請首先閱讀入門指南

                  Flash Player 簡介

Flash Player 像個人助理一樣,幫助您安排復雜的事情,並充當您與外界的接口。但是 Flash Player 自己不會做任何事情,除非您告訴它做什么!有兩種方式做這件事:

  • 時間軸,用 Flash Professional 編寫。這些時間軸是一幀一幀的動畫,表示為一系列標記。標記就是一些簡單的指令,描述在每一幀動畫中做什么事情,比如說在屏幕上移動對象。您在 Flash Professional 中可以做到的每件事情,比如說向幀中添加對象或者設置一個補間,都編碼為 SWF 文件中的一個標記。
  • 腳本,用純 ActionScript 編寫。這些腳本由 Flash Player 在某些特定的時間點來執行,例如在加載 SWF 時、定時器觸發時或者單擊鼠標時。您也可以在 Flash Professional 中將腳本附加到單個幀中。

Flash Player 執行您的腳本或者時間軸中的標記時,這些腳本或標記會告訴它去執行各種活動。這些活動可以大致分為 4 類:

  • 即時活動。這是您告訴 Flash Player 馬上去完成的任務,此時,您的內容將停止執行,直到此操作完成才繼續;例如,創建一個新的位圖或者發出一個 HTTP 請求。
  • 持續活動。在您發起這些任務之后,Flash Player 將在后台持續執行它們,直到完成或者您終止它們。例如,播放音樂或下載文件。
  • 延遲活動。您在腳本或標記中執行的一些似乎不太重要的操作,但是它們會導致 Flash Player 稍后安排一個更大的操作。例如,更改某個顯示對象的位置會將該對象標記為“臟的”,表示它稍后還將由 Flash Player 重新呈現。
  • 隱式活動。這是內置的操作,無需您的請求,Flash Player 會自動執行它們,比如說垃圾收集,或者從操作系統接收鼠標和鍵盤事件。

在閱讀本文時,一定要記住以下內容。Flash Player 不只是執行 ActionScript 代碼,它還執行很多其他活動!如果您將 Flash Player 看作個人助理,那么執行 ActionScript 代碼就像助理在會議中的工作。即使是一個非常簡短的會議,Flash Player 仍然必須提前准備您需要的所有資料。呈現您的精美動畫需要花費時間,大部分真正的工作發生在 ActionScript 代碼之外

                  播放器實例和會話

您可能會感到好奇,Flash Player 怎么管理這些不同活動的執行呢?

您在 Web 瀏覽器中運行 Flash 內容時,Flash Player 通常運行在一個單獨的操作系統進程中。對於運行在該瀏覽器中的所有 SWF(包括任何相關的工人線程),只存在一個進程,所以 Flash Player 管理這些 SWF 的執行,就像它們都是獨立運行的一樣。每個正在運行的 SWF 稱作一個播放器實例,對應於 Scout 中的一個會話。工人線程也是播放器實例。對於 AIR 內容,只存在一個主播放器實例,加上它所使用的任何工人線程。

播放器實例的執行

運行的時候,Flash Player 會在不同的活動之間切換。它可能執行一會兒腳本,然后播放一會兒視頻,然后再呈現一些圖片,等等。具體的順序取決於正在發生什么樣的事件、正在執行哪些持續活動,以及 SWF 的腳本和時間軸請求了哪些活動。在所有這一切的發生過程中,Flash Player 會記錄它所做的事情,並測量消耗的資源,包括 CPU 時間、CPU 內存和 GPU 內存。這些測量值被發送到 Scout,以便您可以看到正在發生的情況。為了最小化這些測量的開銷,Flash Player 只測量和報告那些花費時間或內存較多的活動。

基本上,Flash Player 在任何時間都處於兩種模式之一:

  • 活躍 – 它正在做生產性事情,可以是執行 ActionScript、呈現圖片、處理鼠標事件等。
  • 不活躍 – 它在喝茶休息(免責聲明:千萬別將茶水倒到 Flash Player 上)。這可能是因一些外部條件(如 GPU)而阻塞了,也可能是把所有的工作都干完了。

在 Scout 中,總時間(活躍和不活躍時間之和)用 Frame Timeline 中的灰色豎條表示。為了更加形象地表示 Flash Player 花費在執行各種不同類型活動上的時間比例,Scout 將活躍時間分為 4 大類別:

  • ActionScript(藍色) – 在 ActionScript API 中執行 ActionScript 代碼花費的時間。
  • DisplayList Rendering(綠色)– 執行顯示列表上的操作(比如柵格化和拖曳屏幕)花費的時間。注意,這不包括 Stage3D 呈現,Stage3D 呈現使用的是一個不同的 ActionScript API。
  • Network and Video(黃色)– 通過網絡下載以及流線化和解碼視頻花費的時間。
  • Other(橙色)– 做所有其他事情花費的時間,比如垃圾收集、處理事件和解析 SWF。

一定要知道,Scout 展示的是已逝時間,而不是 CPU 時間。可以把 Flash Player 想象成一個人拿着秒表坐在那里為每個活動計時。如果操作系統在活動中間打斷 Flash Player,讓另一個應用程序運行一會兒,那么 Flash Player 將測量到一個較長的已逝時間。為了得到最精確的數據,在使用 Scout 進行剖析時應該關閉其他應用程序。

Flash Player 的心臟

很多 Flash 應用程序,尤其是游戲,具有大量的圖形動畫。基本原理是以某個頻率(比如說 60 次/秒)重繪屏幕,使之看起來像一個流暢的動畫。為幫助您進行這種編程,Flash Player 具有一個核心概念——。進入播放器的深層,是它的心臟——一個以某種頻率(稱作幀速率)觸發的定時器。這些心跳之間的時間稱作。您需要確定以什么樣的幀速率運行自己的內容。幀速率是 SWF 的一個屬性,您可以在 SWF 運行期間從 ActionScript 動態地更改它。

一種常見的編程方式是,為 Event.ENTER_FRAME(該事件對應於心跳和新幀的開始)注冊一個事件處理程序。如果編寫游戲,您可以使用這個事件處理程序來做保持游戲運行所需的所有周期性工作。例如,可以處理任何用戶輸入,使用物理引擎更新游戲狀態,最后呈現更新后的場景。

使用 Scout 進行剖析時,Frame Timeline 顯示 Flash Player 執行每一幀花費的時間。下面來考慮一款游戲,您想對它獲得 60 幀/秒 (60fps) 的幀速率。一秒鍾等於 1000 毫秒 (ms),所以每一幀的預算時間是 1000/60 = 16.7ms。通常,如果每一幀花費的時間都比這個時間長,那么 Flash Player 將不能以您所需的幀速率運行,您的內容就可能看起來緩慢而抖動。在 Scout 的 Frame Timeline 中,預算時間顯示為一條水平紅線,灰色豎條表示每個幀花費的總時間。

在一個幀中,Flash Player 要執行很多不同的活動。其中首先要做的一件事情就是分派一個 Event.ENTER_FRAME 事件並調用任何已注冊的事件處理程序。在它執行 ActionScript 代碼時,這可能會導致新的延遲活動或持續活動添加到“要執行的活動列表”,比如說重繪部分屏幕或者開始新的文件下載。這些活動必須在這個幀中稍后的某個時間得到執行。此外,幀中還可能出現鼠標、鍵盤及其他事件,這些事件也需要被處理和完成。最終,Flash Player 將完成它對於這個幀的所有任務,不留下任何未完成事情。然后它坐下來等待下一次心跳,以便再次開始一個新的幀!

在 Scout 中,需要關注的主要是越過紅線的彩色豎條。這表示 Flash Player 完成它需要完成的所有事情花費的時間長於整個幀的預算時間。換句話說,Flash Player 在分配給它的時間內有太多的事情要做!如果所有的幀都超出預算時間,那么您的內容通常就會變慢(見圖 1),而如果有很多參差不齊的尖峰,內容就會不流暢。

圖 1:內容始終無法達到目標幀速率。
圖 1:內容始終無法達到目標幀速率。

注意,即使您早早地完成幀,Flash Player 在開始下一幀之前也總是會等待。這是一件好事情——表示您的內容不會一直霸占着設備上的所有資源,這可以降低電力消耗,騰出 CPU 時間來執行其他任務。這也表示您的內容中內置有時差,以便可以按目標幀速率運行在較慢的設備上。灰色豎條徘佪在紅線周圍的情況極為正常(見圖 2)。

圖 2:內容以 60fps 平滑運行
        圖 2:內容以 60fps 平滑運行

關於幀,需要了解一些瑣碎的事情:

  • Flash Player 的最大幀速率是 60fps。設置更高的幀速率也不會讓內容以比這更快的速度運行。記住,大多數顯示器不支持超過 60fps,所以試圖將屏幕幀速率更新得比這快完全是徒勞無功的!
  • Flash Player 盡量與操作系統配合良好。您瀏覽 Web 時,可以有很多的播放器實例在運行,並且幀速率可以各不相同。Flash Player 不是在隨機時間喚醒播放器實例並降慢計算機速度,而是試圖同步不同播放器實例的喚醒時間。大致來講,它每 1/60 秒喚醒一次,並開始任何已就緒播放器實例中的一個新的幀。這表示,如果將幀速率設置為不是剛好平分為 60,比如說設置為 24fps,那么您將看到灰色豎條徘徊在紅線周圍——時上時下(見圖 3)。這是正常的,只要平均上符合目標幀速率就不是問題(您可以在 Scout 中選擇某個范圍的幀,來查看平均幀速率)。
  • 如果在 Scout 中看到灰色豎條一直越過了紅線,即使彩色豎條(活躍時間)總是低於紅線,那么計算機上可能運行着其他應用程序,在使用 CPU 資源。請關閉其他應用程序和瀏覽器窗口。

圖 3:內容以 24fps 平滑運行
        圖 3:內容以 24fps 平滑運行

                  Flash Player 的組件

既然已經較好地了解了 Flash Player 的基本結構,現在就該進一步來了解 Scout 中看到的數據。您會注意到,Top Activities 和 Activity Sequence 面板給出了 Flash Player 正在做的事情的詳細分類。這些面板中的描述盡可能地一目了然,但是有時候您也需要更多的信息來了解問題的根源。本節將 Flash Player 的各種活動划分為幾個功能區域(見圖 4),並對它們的含義提供了更詳細的解釋。

圖 4:Flash Player 的結構
        圖 4:Flash Player 的結構

您可以將本節作為使用 Scout 時的參考指南,只閱讀跟具體問題有關的部分。或者,您也可以閱讀整個這一節,更多地了解 Flash Player 的各個部分是如何工作的。

ActionScript 3 和事件處理

ActionScript 是 Flash 語言;它讓您告訴 Flash Player 去做什么。您建立一個 SWF 時,ActionScript 代碼會被編譯成一種 Flash Player 可以理解的較低級的語言,即 ActionScript 字節碼。當 SWF 運行時,該字節碼會由 ActionScript 虛擬機 (AVM) 執行。AVM 實現 ActionScript 的某些核心功能,包括垃圾收集和異常,還充當您的代碼與 Flash Player 之間的橋梁。

您可以使用 API 從 ActionScript 與 Flash Player 交互。對於您來說,這些 API 就像普通的函數調用,但是在底層,您經常是在調用原生 C 代碼——這里就是奇跡發生的地方!在 Scout 中, 這些 API 在 ActionScript 面板中顯示為普通的函數調用,您無需擔心內部細節。如果在一個 API 調用中花費了太長的時間,您就應該少調用它或者給它分配較少的工作去做(通過縮小參數的大小或復雜度)。

從 ActionScript 調用 Flash Player 時,您需要告訴 Flash Player 何時執行您的代碼。大多數時候,這使用事件處理程序來完成。這些事件處理程序是您注冊的一些函數,旨在特定事件發生時進行調用。您可以創建定制事件,並使用 EventDispatcher.dispatch() 手動調用它們,但是您會經常需要偵聽外部事件,比如說鼠標移動和鍵盤按鍵。Scout 中有很多與此相關的活動:

  • Handling event "<name>" – Flash Player 總是在偵聽和接收來自操作系統的外部事件。接收到一個事件時,它首先必須進行一些處理,然后得出應該調用哪些事件處理程序(例如,對於鍵盤事件,調用的處理程序取決於哪個對象擁有鍵盤焦點)。下面是您會在 Scout 中看到的一些外部事件:
    • 鍵盤事件:keyDown, keyPress, keyUp.
    • 鼠標事件:mouseDown, rightMouseDown, mouseMove, mouseUp, mouseWheel.
    • 觸摸和手勢事件:touch, gesture.
    • 窗口事件:resize, mouseLeave, foreground, background, fullScreen, fullScreenInteractiveAccepted, visible.
  • Event "<name>" – 這是事件 <name> 的實際 ActionScript 事件處理程序。這個事件處理程序只有創建並注冊之后您才會看到(這適用於任何事件,不只是針對上面提到的這些事件)。下面是一個小提示:在 Scout 的 Activity Sequence 或 Top Activities 面板中單擊一個事件,可以過濾 ActionScript 面板,使之只顯示正在這個特定事件處理程序中執行的代碼。

定時器的處理方式非常類似於外部事件,只不過它們是由您自己設置的:

  • Handling event "timer" – 向任何已注冊的事件處理程序分派一個 TimerEvent.TIMER 事件。
  • Timer started:<interval> – 表明定時器已經啟動,定時器事件之間的延遲是 <interval> ms。

其中也會有很多與執行您的代碼及支持 ActionScript 語言特性相關的 AVM 活動:

  • Garbage collection – 與 C 以及其他需要您來進行內存管理的語言不同,ActionScript 會為您進行內存的收集和釋放。Flash Player 會定期地運行垃圾收集器,以掃描任何不再被引用的對象(沒有誰引用它們了),並釋放它們在使用的內存。如果您的內容花費大量的時間進行垃圾收集,那么表明您創建了太多的對象。您可能試圖想要高速緩存或池化對象,避免總是創建新的實例。
  • Trace:<output> – 這給出 ActionScript 代碼中任何 trace() 語句的輸出。也可以在 Trace Log 面板中查看該數據。
  • Exception:<class> – 這給出對 ActionScript 代碼執行期間出現的任何異常的堆棧跟蹤。即使異常被捕獲並處理了,這也會顯示出來,所以在 Scout 中看到這樣的信息並不一定代表您的代碼有問題。
  • Handling uncaught error – 向任何已注冊的處理程序分派一個 UncaughtErrorEvent.UNCAUGHT_ERROR

最后,如果您從其他語言調用了 ActionScript 3 代碼,還會看到一些活動:

  • AVM Bridge callback – 從 ActionScript 2 對 ActionScript 3 代碼的調用。
  • ExternalInterface callback – 從 JavaScript 代碼對 ActionScript 3 代碼的調用。

ActionScript 工人

如果您在內容中使用了 ActionScript 工人,那么它們將作為單獨的會話出現在 Scout 中。在底層,它們實際上就是單獨的播放器實例,帶有一個用於在它們之間共享數據的 API。在 Scout 中,對工人的支持目前還是一個 beta 特性。為啟用支持,您需要做以下事情:

  • 在 Scout 中,單擊 Preferences > Beta Features,並選擇 Start Sessions For ActionScript Workers。
  • 在 Scout 的側欄中(如果不可見,請單擊 Window > Sidebar),確保 ActionScript Sampler 已啟用。
  • 確保您的內容已經選擇為支持遙測技術(如果不知怎么做,請參見入門指南)。

對於使用 ActionScript 工人的內容,您可能會在 Scout 中看到以下活動:

  • Running Worker code – 如果啟動一個工人,並繼續運行其中的一些 ActionScript 代碼(工人執行單個長時間運行的函數,而不是由事件驅動的),您將在 Scout 中看到此活動。這表示您仍然處於工人的頂層入口點,所以它不對應於任何事件。
  • Waiting for Condition – 在對 Condition.wait() 的調用上阻塞。
  • MessageChannel.receive – 在對 MessageChannel.receive() 的調用上阻塞。
  • MessageChannel.send – 在對 MessageChannel.send() 的調用上阻塞。
  • Mutex.lock – 在對 Mutex.lock() 的調用上阻塞。
  • Mutex.trylock – 在對 Mutex.trylock() 的調用上阻塞。

用戶活動

除了 Flash Player 所做的內置測量,您還可以使用 Telemetry API 向 Scout 發送定制數據。兩個主要的調用如下:

  • Telemetry.sendMetric(name, value) – 這向 Scout 報告一個名-值對,將顯示在 Activity Sequence 面板中。例如,如果調用 Telemetry.sendMetric("UserID", 2),您將在代碼執行時看到 "UserID:2" 出現在 Activity Sequence 面板中。
  • Telemetry.sendSpanMetric(name, startTime, value) – 這向 Scout 報告一個活動,帶有一個可選值。您通過查看 Telemetry.spanMarker 記錄開始時間,然后在您想要測量的周期末尾將之傳遞到 Telemetry.sendSpanMetric。它將同時顯示在 Scout 的 Top Activities 和 Activity Sequence 面板中。

有關更多信息,以及如何使用該 API 的例子,請參見 關於 Telemetry API 的文檔

幀滴答器

Flash Player 的核心是幀滴答器–每當一個新幀開始時發生的心跳。在每個幀的開始,它都會執行任何時間軸標記,調用時間軸上的任何幀腳本,並分派一些關鍵的 ActionScript 3 事件。幀滴答器的活動如下:

  • Running SWF tags for frame – Flash Player 執行與該幀相關的 SWF 標記。這通常對應於您在 Flash Professional 所做的事情;例如,創建附加到幀的對象,並根據補間移動這些對象。
  • Running frame actions – 這些是您在 Flash Professional 中使用 Actions 面板附加到時間軸上某個幀中的腳本。該腳本可以是 ActionScript 2 或 ActionScript 3。對於 ActionScript 3,可能不會被立即執行,而是被放置在幀結束之前 Flash Player 會執行的一個隊列中。
  • Running AS3 attached to frame – 執行任何在 Flash Player 運行附加到時間軸上的某個幀上的腳本時放在隊列中的 ActionScript 3 代碼。
  • AS2 event "enterFrame" – 用 ActionScript 2 編寫的 MovieClip.onEnterFrame 事件處理程序。
  • Handling event "enterFrame" – 當一個新幀開始時,Flash Player 尋找並調用 Event.ENTER_FRAME 的任何已注冊處理程序。
  • Handling event "frameLabel" – 當一個新幀進入后,如果該幀具有一個 FrameLabel 對象,且該對象為 Event.FRAME_LABEL 帶有已注冊處理程序,那么該處理程序就會被調用。
  • Handling event "exitFrame" – 在當前幀臨結束之前,Flash Player 尋找並調用 Event.EXIT_FRAME 的任何已注冊處理程序。

顯示列表呈現

顯示列表是 Flash Player 中經典的呈現方法。簡單來說,給您一個稱作畫布的空白屏幕,您通過附加和布置稱作顯示對象的圖形實體,在畫布上繪畫。有幾種不同類型的顯示對象,包括矢量插畫、位圖和文本,它們可以逐層嵌套來構成復雜的場景。無論是從 ActionScript 與顯示對象交互,還是通過在 Flash Pro 中布置它們和設置補間,您都無需擔心它們是如何真正呈現的。Flash Player 為您完成這項艱難的工作,計算出如何將顯示列表轉換成您在屏幕上看到的實際像素。

如果是使用 ActionScript 來操縱顯示列表,您會注意到有好幾種 API 您可以調用來進行更改。您可以將這些更改看作為即時活動。您調用 API 時,它會立即修改顯示列表的內部狀態。它做的是立即修改您在屏幕上看到的內容!相反,Flash Player 將您修改的任何顯示對象標記為臟的,這表示它們需要重繪。稍后某個時候,在您的代碼執行完成之后,Flash Player 將執行一個呈現通道。它將所有的更改收集到一起,因此只需更新一次屏幕,大大提高了效率。您可以將這想象成單格拍制動畫。在一個幀當中,您到處捏制好所有的小泥人,完成之后,Flash Player 再來拍攝這一幀的場景圖。

圖 5:Flash Player 中的一個呈現通道
圖 5:Flash Player 中的一個呈現通道

這就是呈現通道在 Scout 中的樣子(見圖 5):

  • 計算臟區域 – 呈現循環開始於掃描顯示列表,尋找任何被標記為臟的對象(例如,移動了的對象)。然后它最多生成三個矩陣,其中包含所有的臟對象。 這些是需要重繪的屏幕區域。您可以在 Scout 的 DisplayList Rendering 面板中看到這些區域,即紅色區域。
  • 呈現臟區域 – 對於每個臟區域,Flash Player 必須計算出新像素應該是什么樣的。它通過在一個稱作顯示緩沖區的內部緩沖區中構造場景來做到這一點。這個過程涉及到一些步驟:
    • 從 DisplayObject 構建邊緣– Flash Player 再次掃描顯示列表,尋找與臟區域重疊的顯示對象。對於找到的每個這樣的對象,它會將之分解成一組邊緣填充(填充可以是純色、漸變或者定義邊緣之間的空間如何上色的位圖)。如果顯示對象包含其他顯示對象,就會對它們遞歸地應用這同一過程。本質上,Flash Player 將分層的場景平面化為不同的填充區域。
    • 柵格化邊緣 – Flash Player 現在必須將邊緣和顏色轉換成實際的像素。它將臟區域分解成稱作掃描線的水平線,每個垂直像素一條。掃描線在機器內核之間共享。Flash Player 沿着每條掃描線,根據這條線周圍的邊緣之間的區域的填充,計算出覆蓋每個像素的顏色是什么。這個過程花費的時間取決於像素數量以及繪制每個像素的成本。繪制純色的代價最低,而繪制帶有多層 alpha 混合的旋轉和縮放位圖則相當昂貴!
    • 應用過濾器:<name> – 如果您設置了任何過濾器,比如光暈或投影效果,那么這些過濾器現在將被應用於已柵格化的圖像。但是對於子顯示對象的過濾器,則必須在父對象被柵格化之前加以應用。Flash Player 通過將子顯示對象柵格化到一個單獨的表面(在 Scout 的 DisplayList Rendering 面板中顯示為藍色),並將過濾器應用於該表面,來做到這一點。這將導致一個新位圖,該位圖在為父顯示對象構建邊緣時被當作一個原子填充。
  • 復制到屏幕 – 在呈現通道的末尾,Flash Player 從它的顯示緩沖區將數據復制到屏幕。這個過程也叫做展示。該過程花費的時間主要取決於必須復制的像素數量,但是在從操作系統獲得鎖的過程中也可能存在延遲。如果您使用了硬件加速,就意味着顯示緩沖區已經在顯卡上了,Flash Player 只需告訴顯卡顯示它即可。這應該顯示為 Waiting for GPU 時間(參見關於 GPU 相關性能問題的一些注意事項,了解更多信息)。如果不是這樣的,請檢查您是否使用的是最新版 Flash Player(11.5 或更晚版本)。

如果繪制帶有很多邊緣和不同過濾器的復雜形狀,那么呈現將會很昂貴。請將顯示列表修改為盡可能地小,以減小每個幀中需要重繪的臟區域的數量和大小。如果是繪制一個很少修改(只四處移動)的復雜的嵌套對象,那么您可以通過高速緩存它來改善呈現性能。這會將顯示對象柵格化到一個高速緩存表面,該表面發生更改的話只需重新生成即可。如果只平移顯示對象,那么請設置 cacheAsBitmap 屬性。如果還想要對它進行旋轉和縮放,那么請使用 cacheAsBitmapMatrix(只在手機上受 AIR 支持)。

Scout 顯示以下與高速緩存表面(在 DisplayList Rendering 面板中顯示為橙色)相關的活動:

  • Rendering from cached surface – 如果顯示對象具有有效的高速緩存,那么 Flash Player 將使用該活動,而不是構建並柵格化它的邊緣。
  • Updating cached surface – 如果您修改已高速緩存的顯示對象或者它的子對象,那么將使高速緩存失效,需要重新創建它。如果 Flash Player 花費太多的時間更新高速緩存表面,就表明您使用高速緩存不當。高速緩存應該只使用於很少更改的顯示對象,否則不進行緩存更快一些!

作為每個呈現通道的一部分,Scout 將顯示關於在某些特定類型的顯示對象上執行的活動的附加信息。以下活動與呈現文本相關:

  • Updating text layout – 一個文本顯示對象發生了更改(例如,更改了 TextElement 或 TextField 對象的文本屬性),需要再次布局。這也會將顯示對象標記為臟的,以便在下一個呈現通道中得到重繪。
  • Rendering text – 使用 TextField 類呈現文本。
  • Rendering FTE text – 使用 flash.text.engine 包中的 Flash Text Engine 類呈現文本。

以下活動與 Bitmap 對象及操縱它們的相關位圖數據 (Bitmap.bitmapData) 相關:

  • Creating BitmapData – BitmapData 對象的構造函數。這只出現在 Scout 的 DisplayList Rendering 面板中。
  • Decompressing images – 您加載壓縮圖像時,比如 JPEG 或 PNG,需要先進行解壓,然后 Flash Player 才能顯示它。如果覺得這個過程花費的時間太長,您可以告訴 Flash Player 使用 異步解碼,異步解碼是使用一個后台線程在加載的過程中解壓圖像,所以不會阻塞您的 UI。
  • BitmapData.copyPixels – 對 BitmapData.copyPixels() 的調用。
  • BitmapData.draw – 對 BitmapData.draw() 的調用。

您可能會在 Scout 中遇到幾個與顯示列表呈現相關的額外操作:

  • Creating Display Buffer – 該過程創建顯示緩沖區,Flash Player 將顯示列表柵格化到該緩沖區。這通常應該在播放器實例創建時發生一次。
  • Resizing Display Buffer – 該過程調整顯示緩沖區的大小,一般在畫布重新調整大小時進行調整。
  • Triggering UpdateAfterEvent – 一個呈現通道將要開始,因為您調用了 updateAfterEvent()。一般來說,如果設置了較高的幀速率(比如 30fps 或 60fps),那么您應該永遠不需要調用該函數,因為 Flash Player 已經每個幀至少執行了一個呈現通道。
  • Handling event "render" – 如果您顯式地調用 stage.invalidate(),那么在呈現通道臨開始之前,一個 Event.RENDER 事件將被分派給任何已注冊處理程序。

Stage3D 呈現

Stage3D 是一個基於 OpenGL 和 DirectX 的 ActionScript API,可以實現跨平台的硬件加速呈現。它的工作方式完全不同於傳統的顯示列表模型,盡管Starling 框架運行在 Stage3D 之上,並向 2D 內容顯示列表提供一個類似的 API。Stage3D 內容呈現到它自己的顯示緩沖區,在顯示列表之后出現在屏幕上。

Stage3D 呈現循環的基本結構是,首先設置 GPU 的狀態(上傳您想要使用的紋理、網格和着色器),然后發出很多繪制調用,用於告訴 GPU 向目標緩沖區呈現大批的三角形。場景構造完之后,調用 Context3D.present() 以將它真正顯示到屏幕上。實際工作發生的地方有兩個:

  • 在 ActionScript API 內 – 使用 flash.display3D 包發出的調用。要了解這會花費多長時間,請在 Scout 中打開 ActionScript Sampler。在 Summary 面板中打開 ActionScript 類別,可以看到在 Stage3D API 中花費了多長時間,在 ActionScript 面板中還可以找出哪些調用是最慢的。
  • 在 GPU 上 – 最終還是顯卡完成絕大部分工作,比如實際的呈現。Scout 目前不提供任何關於 GPU 處理時間的信息,但是您可以在 Summary 面板中查看 GPU 內存使用情況。如果您認為瓶頸在 GPU 上,請參見下面這一節關於 GPU 相關性能問題的內容。

SWF 加載器

開始一個新的播放器實例時,Flash Player 首先必須下載、解析主 SWF,並將之加載到內存中,然后才能開始執行它。您將在 Scout 中看到與之相關的以下活動(注意,與 Flash Player 的網絡組件有一定程度的重疊):

  • Loading SWF:<url> – Flash Player 將要加載的主 SWF 的 URL。運行獨立版本的 Flash Player 時會看到這個 URL。
  • Loading file:<url> – 在給定 URL 下載文件。
  • Receiving Loader data – 通過網絡接收您在使用 Loader.load() 加載的資源的數據。每次接收到數據時,一個 ProgressEvent.PROGRESS 事件都會被分派給任何已注冊處理程序。
  • Receiving SWF data – 接收一些作為 SWF 文件一部分的數據。
  • Receiving image data – 接收一些作為圖像(比如 JPEG、PNG 或 GIF)一部分的數據。
  • Decoding SWF file – 解析 SWF 數據,並根據需要解壓縮。
  • Closing Loader – 關閉加載器使用的網絡連接。導致該活動的原因可能是加載完成、發生錯誤或者調用 Loader.close()
  • Preparing ActionScript bytecode – 解碼 ActionScript 字節碼,使之成為可執行的形式。
  • Initializing AS globals – 初始化在任何函數或類定義之外定義的變量。
  • Handling event "onLoadInit" – 用 ActionScript 2 編寫的 MovieClipLoader.onLoadInit 事件處理程序。

網絡

Flash Player 支持的網絡操作有三種主要類型:本地連接、TCP/HTTP 連接和流媒體。無論使用哪種類型,開始一般是設置網絡連接或發出請求。網絡操作比較費時,所以不是同步發生的。一旦您發出了請求,ActionScript 代碼就可以在 Flash Player 在后台處理網絡操作的同時執行其他任務。通過為相關事件注冊事件處理程序,可以了解此操作何時完成或者狀態發生更新。

要讓同一台機器上的兩個播放器實例進行通信(例如,假設加載了一個幫助器 SWF),可以設置一個 LocalConnection 對象,來讓一個實例調用另一個實例的函數。Scout 顯示以下與本地連接相關的活動:

  • LocalConnection callback – 由於在發送 LocalConnection 對象時調用了 send(connectionName, functionName, arguments),所以在接收 LocalConnection 對象時要調用 functionName(arguments)
  • Handling LocalConnection traffic – 執行本地連接的一般維護,包括發送和接收 LocalConnection 對象的任何數據。

想要下載內容時,比如圖像和其他 SWF,您通常使用 Loader 或 URLLoader 對象,它們使用 HTTP。您也可以使用 HTTP NetConnection 從服務器發送或接收數據。Scout 顯示以下與下載相關的活動:

  • Sending URL requests –利用諸如 NetConnection.call() 之類函數通過 HTTP 發出的請求由 Flash Player 進行排隊。這些排好隊的請求定期地通過網絡發送出去。
  • URL request timestamp – 發送 URL 請求的時間點。
  • URL request:<url> – Flash Player 正在請求的 URL。
  • URL request ID:<id> – URL 請求的惟一識別符。
  • Responder callback – 由於 NetConnection.call() 成功或返回錯誤,所以調用一個您傳遞給 Responder 對象構造函數的函數。
  • Processing network buffers – 一般網絡開銷,比如說遍歷緩沖區以查看是否有任何數據達到。

Flash Player 也為來自服務器的流媒體(比如音頻和視頻數據)支持很多協議。可以使用 NetConnection 創建 NetStream,並指定協議。實時消息傳遞協議 (RTMP) 運行在 TCP 之上,實時媒體流協議 (RTMFP) 運行在 UDP 之上。在 Scout 中可以看到以下與流式傳輸相關的活動:

  • Receiving NetStream audio/video – 從服務器接收大量音頻或視頻數據。
  • Decoding media from network – 解壓縮通過網絡接收到的音頻或視頻數據,以便可由 Flash Player 播放。
  • Receiving NetStream metadata – 接收關於所請求媒體的元數據,比如創建時間、持續時長、主題等。通過設置 NetStream.client 對象的 onMetaData 屬性,可以注冊一個在接收到元數據時調用的事件處理程序。
  • Receiving NetStream commands – 處理來自服務器的命令消息。例如,這些命令通知 Flash Player 關於其以前請求的命令的狀態。
  • Closing network connection – 導致該活動的原因可能是流傳輸結束、發生錯誤或者調用 NetStream.close()
  • Receiving NetStream shared objects – 接收存儲在服務器上(以便可以跨多個客戶端共享)的 ActionScript 3 對象的數據。這可能是由於調用了 SharedObject.connect()

聲音

在多數台式機上,Flash Player 播放聲音的效率很高。當對諸如 Sound 或 SoundChannel 之類的對象調用 flash.media 包中的函數時,執行聲音操作花費的時間將顯示在 Scout 的 ActionScript 面板中。您也可能看到下面這個活動:

  • Dispatching Sound Complete – 用 ActionScript 2 編寫的 Sound.onSoundComplete 處理程序,或者是用 ActionScript 3 編寫的針對 SoundChannel 對象的 Event.SOUND_COMPLETE 處理程序。

視頻

Flash Player 中的視頻播放基本上是一個長期運行的網絡操作。當您將想要播放的視頻告訴 Flash Player 時,Flash Player 就會在后台發起一個持續活動。通過網絡定期到達的數據被解壓,然后顯示在屏幕上。取決於平台、編解碼器以及其他視頻設置,需要的 CPU 時間差異很大。

Scout 目前不提供關於視頻性能的很多信息,但是您可能會看到下面這個活動:

  • Initializing StageVideo – 設置 StageVideo 對象。

其他活動

還有一些無法歸為以上類別的其他 Flash Player 活動:

  • Running AS2 – 執行 ActionScript 2 代碼。即使您沒有編寫任何 ActionScript 2 代碼,也可能顯示該活動,因為 Flash Player 會自動生成一些 ActionScript 2 代碼來執行某些初始化和內務處理功能。
  • AIR startup – 在啟動時為 AIR 應用程序運行 ActionScript 代碼。
  • Button hit testing – Flash Player 具有一些特殊的代碼來處理老式的按鈕對象(在 Flash Professional 中創建的對象)。除了為鼠標事件尋找 ActionScript 事件處理程序之外,每當鼠標移動時 Flash Player還會為這些按鈕搜索顯示列表。如果顯示列表中有大量的對象,那么這個搜索過程將很昂貴。不幸的是,即使沒有使用老式按鈕對象,也會發生這一操作,不過 Adobe 正在着手解決這個問題。
  • Runtime overhead – 這包括任何其他活動沒有計算在內的時間。Flash Player 只測量通常花費大量時間的活動,所以它做的所有其他小事情都歸為這一類中。這類活動使用的時間不多,可以忽略不計,您無需關注它。

不活躍時間

Flash Player 在等待發生某事情時,Scout 中顯示為不活躍時間。這可能是因為在某件事情上阻塞了,或者只是已經完成了所有的工作。展開 Scout 的 Summary 面板中的 Inactive 類別,會看到這個時間分為以下部分或所有種類:

  • Waiting for next frame – 當前幀中沒有事情可做了,所以播放器實例進入睡眠,直到開始下一幀;或者發生了一些外部事件(比如鼠標或網絡事件)。
  • Waiting for GPU – Flash Player 在等待顯卡執行某些操作。參見關於 GPU 相關性能問題的一節內容,了解詳細信息。
  • Waiting for condition – 工人線程阻塞在 Condition.wait()MessageChannel.send()MessageChannel.receive() 調用上。

                 Scout 中的內存報告

就跟測量執行各種活動花費了多長時間一樣,Flash Player 也跟蹤它在使用多少內存。在 Scout 的 Summary 面板中可以看到該報告,就像 CPU 時間一樣,內存使用情況也分為幾類。Flash Player 只跟蹤它顯式分配的內存——有一些內存是操作系統分配的,比如說用於存儲 Flash Player 可執行文件的內存,這不會顯示在 Scout 中。

在 Scout 中查看一個會話的內存使用情況時,一定要明白總內存所有正在運行的播放器實例使用的內存。其他播放器實例使用的內存出現在 Other 類別中,位於 Other Players 下面。此外,Uncategorized 類別下的一些內存可能是其他播放器實例使用的。一些數據結構在 Flash Player 中是共享的,因而難以歸屬於特定的播放器實例。在瀏覽器中剖析內容時,可能的話最好只運行 SWF。

Flash Player 盡力跟蹤使用大量內存的主要數據結構,但是某些區域比另外一些區域涵蓋得更好一些。目前,它測量音頻和視頻緩沖區或者 JITted ActionScript 代碼使用的內存。這將和任何其他未跟蹤的內部數據結構一起顯示在 Uncategorized 類別下。

                 關於 GPU 相關性能問題的一些注意事項

GPU 是顯卡上的專用處理器,用於硬件加速呈現。它擅長於大規模並行計算,比如說定位和着色巨大的三角形陣列。這是與 CPU 進行的比較,CPU 可以完成更復雜的任務,但是只擅長於一次做一件事情。Stage3D 通過提供一個 ActionScript API 來利用 GPU,該 API 的作用是讓您可以直接與之溝通。為了工作更為輕松,通常會用到一個較高級別的框架,比如針對 2D 內容的 Starling 和針對 3D 內容的 Away3D。但是請記住,這些框架是在幕后使用 GPU。

使用 Stage3D 時要記住的一件事情是,GPU 與 CPU 並行運行。這意味着,內容的速度局限於那個最慢的組件。即使您的 ActionScript 代碼很快(所以 CPU 沒有超載),如果分派 GPU 太多的工作,那么也不會得到您想要的幀速率。下面是使用 GPU 時的兩個最常見性能問題:

  • 您可能在要求 GPU 去做太多的工作。最常見的原因是發出太多的繪制調用,更改 GPU 狀態太頻,或者使用太大的網格和紋理。有大量優化內容的方法。例如,使用壓縮紋理,在 Starling 中使用紋理貼圖集, 當然也包括簡化試圖呈現的對象。目前,Scout 還顯示不了 GPU 正在做什么,但是您可以使用 Scout 的 Stage3D Rendering 面板來查看正在執行什么 Stage3D 命令,並尋找優化的方法。
  • 您可能在嘗試比 GPU 可以達到的速度更快地更新屏幕。顯卡以一種稱作雙緩沖的技術工作。一個緩沖區包含當前顯示在屏幕上的數據,而另一個緩沖區(后台緩沖區) 用於構造下一個要顯示的圖像。調用 Context3D.present() 時是在告訴 GPU 顯示您所繪制的內容,意味着 GPU 應該輪換這兩個分區。但是 GPU 每 1/60 秒才對它們輪換一次。這就是所謂的 VSync,它跟顯示器更新圖像的速度相關。如果 GPU 不能輪換,它將阻塞,這在 Scout 中顯示為 Waiting for GPU。這通常不是問題,但是如果 CPU 跟不上 VSync 速率,它可能再三地錯過最后期限,然后必須等待下一個 VSync,GPU 才能更新。如果發生此情況,通常可以通過設置較低的幀速率來避免額外的等待時間,從而改善性能。

下面是一個重要的提示:如果您的內容在 Scout 中顯示過多的 Waiting for GPU 時間,請嘗試在 Flash Player 中臨時禁用硬件加速(右擊並選擇 Settings)。這將導致 Flash Player 退回到軟件呈現,等待時間將會消失,所以您可以確認問題是 GPU 相關的。

                  下一步閱讀方向

既然更多地了解了 Flash Player 的工作方式,那么接下來最好能夠使用 Adobe Scout 來優化內容。記住,如果在使用 Scout 時忘記了一些指標的含義,您可以使用本文作為參考。祝您學習快樂!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM