2011-04-28 9 views
18

我正在測量一個簡單的WPF動畫幀之間的時間。穿孔器說應用程序的執行速度爲〜60fps,所以我預計幀之間的時間約爲16.6ms,而且幾乎沒有偏差。爲什麼WPF中的幀頻不規則並不限制顯示器刷新?

public MainWindow() 
    { 
    ... 
     CompositionTarget.Rendering += Rendering; 
    } 

    List<long> FrameDurations = new List<long>(); 
    private long PreviousFrameTime = 0; 
    private void Rendering(object o, EventArgs args) 
    { 
     FrameDurations.Add(DateTime.Now.Ticks - PreviousFrameTime); 
     PreviousFrameTime = DateTime.Now.Ticks; 
    } 

有兩件事情讓我很吃驚:幀之間

  • 時間相當不規則
  • 幀之間的時間是8ms的〜。我本來期望顯示器的刷新速率會在幀之間設定一個較低的時間限制(即每幀之間60Hz = 16.6ms,而任何速度都是毫無意義的)。

DefaultFrameRate

Ÿ - 幀計數

可能的混雜因素

  • 定時誤差
  • 如果CompositionTarget - 在蜱框架(10,000蜱= 1毫秒)
    X之間的時間。渲染實際上並不涉及單個框架的繪製

我使用的項目:馬庫斯指出,我可以用RenderingEventArgs.RenderingTime.Ticks代替DateTime.Now.Ticks SimpleWindow.zip

===編輯

。我重複了跑步,得到了非常不同的結果。唯一的區別是定時方法:

DateTime.Now.Ticks

DefaultFrameRate

RenderingEventArgs.RenderingTime.Ticks

enter image description here

數據從RenderingEventArgs產生的數據更接近預計16.6ms /幀,並且是一致的。

  • 我不知道爲什麼DateTime.Now和RenderingEventArgs會產生這樣非常不同的數據。
  • 假設RenderingEventArgs正在產生正確的時間,那些時間不是預期的16.6ms仍然有點令人不安。

如果顯示屏每16.6ms更新一次並且WPF每14.9ms更新一次,那麼我們可以預料到一個競爭條件會導致撕裂。也就是說,當顯示器試圖讀取圖像時,大約每10幀WPF都會嘗試寫入圖像。

+0

秒錶類是去避免計時器誤差 – 2011-04-28 01:12:30

+0

這種「可能」是有益的方式:http://rhnatiuk.wordpress.com/2008/12/21/wpf-video-playback-problems/ - 我不確定這個問題有多相關,但我認爲它今天仍然存在。 – 2011-04-28 07:03:53

+3

@Tristan @Martin時間測量根本不需要!你可以[將EventArgs轉換爲RenderingEventArgs來獲取RenderTime](http://msdn.microsoft.com/en-us/library/system.windows.media.compositiontarget.shipping(VS.95).aspx) – 2011-04-28 10:32:54

回答

19

我提出與WPF隊這個問題,這裏是響應的總結,我給出:

計算從UI線程 幀率是困難的。 WPF從渲染線程中分離出 UI線程。 UI線程將呈現:

  • 不管什麼時候被標記爲髒,我們流失降至渲染 優先。這比刷新率更經常發生 。

  • 如果動畫正在等待(或者,如果有人迷上 CompositionTarget.Rendering事件),我們將 每 從目前的渲染線程渲染後的UI線程。 這涉及提前計時 樹,以便動畫計算其新的 值。

正因爲如此,該 CompositionTarget.Rendering事件可以 提高每「幀」多次。 當報告的 幀時間發生變化時,我們報告 中的預期「幀時間」RenderingEventArgs和 應用程序應該只執行「每幀」 工作。

注意,UI線程正在做許多事情 ,所以它是不可靠的 承擔CompositionTarget.Rendering 事件處理程序在一個可靠的 節奏運行。我們使用的模型(將這兩個線程去耦爲 )意味着線程可能稍微落後,因爲它正在計算未來幀時間的動畫。

特別感謝Dwayne需要向我解釋這一點。

+5

太棒了。我在哪裏可以學到更多這樣的東西?有一本好書嗎?我一直無法找到'引擎蓋下'的WPF信息。一個問題是,因爲WPF很容易(相對於GDI +等),所以有很多人寫這篇文章的人並不真正瞭解他們在做什麼,但仍然寫下了它。似乎有一小部分質量差的信息需要過濾掉。 – Tristan 2011-06-06 18:43:47

2

WPF沒有被設計成一個恆定幀率渲染系統。當屏幕中的元素被標記爲已更改時,WPF將呈現屏幕。渲染系統作爲消息循環運行,因此無法確保以特定間隔渲染幀。

+0

你的答案是有道理的,並且與我看到的所有內容都一致。你能提供任何數據或手段來備份你的斷言嗎? (此外,感謝您的時間,我非常感謝) – Tristan 2011-05-11 18:05:03

+1

您可以從WPF的一位設計人員的第9頻道的WPF體系結構中檢查此視頻(他們談論了很多關於Avalon的內容,它是WPF之前的代碼名稱發佈)[鏈接](http://channel9.msdn.com/Shows/Going+Deep/Greg-Schechter-Windows-Presentation-FoundationWPF-Architecture) – Vicro 2011-05-12 02:38:05

+1

我還發現了一些關於[MSDN]的信息(http:// msdn .microsoft.com/en-us/library/ms748373.aspx#visual_rendering_behavior)有關渲染行爲。 – Vicro 2011-05-12 02:40:33

8

首先 - 「克里斯托弗Bennage's應答有一個很好的解釋,並提供了一個暗示,一個解決方案:

‘只有做到‘每幀’工作的上報幀時間改變’

這是一個有點難,因爲RenderingEventArgs被隱藏爲正常的EventArgs並且必須完成強制轉換。

爲了使這個有點easyer,一個方便易解決方案可以在「埃文的CODE舊車」 http://evanl.wordpress.com/2009/12/06/efficient-optimal-per-frame-eventing-in-wpf/

發現我把他的代碼,並修改它一下。現在只是把我剪斷,將該類添加到您的項目,使用CompositionTargetEx是你使用CompositionTarget和你的罰款:)

public static class CompositionTargetEx { 
    private static TimeSpan _last = TimeSpan.Zero; 
    private static event EventHandler<RenderingEventArgs> _FrameUpdating; 
    public static event EventHandler<RenderingEventArgs> Rendering { 
     add { 
      if (_FrameUpdating == null)     
       CompositionTarget.Rendering += CompositionTarget_Rendering; 
      _FrameUpdating += value; 
     } 
     remove { 
      _FrameUpdating -= value; 
      if (_FrameUpdating == null)     
       CompositionTarget.Rendering -= CompositionTarget_Rendering; 
     } 
    } 
    static void CompositionTarget_Rendering(object sender, EventArgs e) { 
     RenderingEventArgs args = (RenderingEventArgs)e; 
     if (args.RenderingTime == _last) 
      return; 
     _last = args.RenderingTime; _FrameUpdating(sender, args); 
    } 
}