2013-05-10 70 views
4

我正在使用D3DImage來顯示一個接一個呈現給同一個Direct3D表面的幀序列。我的當前邏輯是這樣的:如何檢測WPF控件何時被重繪?

  • 顯示上次渲染的幀(ieD3DImage.Lock()/ AddDirtyRect()/解鎖())
  • 開始呈現下一個幀
  • 等待下一幀做好準備和是時候來顯示它
  • 顯示上次渲染的幀
  • ...

這種方法的問題是,當我們完成了對D3D呼喚解鎖()圖像中,圖像實際上並未被複制,只是計劃在下一個WPF呈現中複製。因此,在WPF有機會顯示它之前,我們可能會在Direct3D表面上渲染一個新框架。最終結果是我們在顯示器上看到錯過的幀。

現在,我使用一個單獨的Direct3D紋理渲染和「顯示質感」只是顯示之前,即給予更好的結果,但會帶來巨大的開銷執行復制的實驗。最好能夠知道D3DImage何時刷新並開始渲染下一幀。這是可能的,如果是這樣的話?或者你有更好的主意嗎?

謝謝。

+0

是否'CompositionTarget.Rendering'幫幫我? – 2013-05-10 14:19:17

+0

我的理解是,這個事件在渲染之前被調用,而不是在之後。如果我們必須等待下一個渲染事件才能知道前一個渲染已完成,那麼開始渲染幀已經太晚了。理想情況下,我們希望在重繪D3DImage或其包含的UIElement後立即調用WPF的渲染線程。 – Asik 2013-05-10 14:22:44

+0

你可以做你所有的渲染工作的時間提前(急切地),但是使用'CompositionTarget.Rendering'作爲觸發調用'解鎖()'(如果你的渲染就緒)您'D3DImage',並安排另一渲染?這意味着您只能在WPF的幀速率下渲染和顯示圖像*最多*(但可能小於)。 – 2013-05-10 15:04:23

回答

0

它看起來像這樣乾淨的方式,爲了與UI並行渲染,是渲染到一個單獨的D3D表面,並將其複製到顯示錶面(即傳遞給SetBackBuffer的表面)調用Lock()和Unlock()。因此算法變爲:

  1. 複製並顯示最後呈現的幀,即從渲染到顯示器表面
  2. SetBackBuffer(displaySurface)
  3. AddDirtyRect()
  4. Unlock()
  5. 附表
    • Lock()
    • 複製一個新的渲染到渲染表面
    • 等待它完成,並且時機可以顯示
    • 轉到1

爲D3DImage explicitely states的文檔:

而D3DImage解鎖不更新的Direct3D表面。

這裏的痛點是複製,這可能是昂貴的(即如果硬件繁忙時> 2ms)。爲了使用顯示器表面,而D3DImage被解鎖(避免潛在的昂貴的操作在渲染時),人們將不得不求助於拆卸和反射掛鉤到D3DImage自己的渲染......

0

當WPF要呈現時,CompositionTarget.Rendering event被調用,所以這就是當你應該做你的Lock()Unlock()。在Unlock()之後,您可以啓動下一個渲染。

您也應該檢查RenderingTime,因爲該事件可能每幀觸發多次。嘗試是這樣的:

private void HandleWpfCompositionTargetRendering(object sender, EventArgs e) 
{ 
    RenderingEventArgs rea = e as RenderingEventArgs; 

    // It's possible for Rendering to call back twice in the same frame 
    // so only render when we haven't already rendered in this frame. 
    if (this.lastRenderTime == rea.RenderingTime) 
     return; 

    if (this.renderIsFinished) 
    { 
     // Lock(); 
     // SetBackBuffer(...); 
     // AddDirtyRect(...); 
     // Unlock(); 

     this.renderIsFinished = false; 
     // Fire event to start new render 
     // the event needs to set this.renderIsFinished = true when the render is done 

     // Remember last render time 
     this.lastRenderTime = rea.RenderingTime; 
    } 
} 

更新,以解決意見

你肯定有一個競爭條件? This page表示當您撥打Unlock()時,後臺緩衝區會被複制。

如果確實存在競爭條件,那麼在渲染代碼周圍放置鎖定/解鎖如何? This page表示Lock()將阻止,直到複製完成。

+0

正如我在評論中所說,這並不能消除競爭條件。渲染事件在UI線程上調用,但實際的刷新發生在事件發生後的一些(小)時間內的WPF渲染線程上。因此,在Unlock()之後調度渲染器總是會在渲染幀之前覆蓋該幀。 – Asik 2013-05-10 16:39:58

+0

@Asik請參閱我的更新。 – shoelzer 2013-05-10 16:52:54

+0

是的,有文檔建議的競爭條件(它被「標記爲渲染」),我已經看了反射器中的代碼。我也以大約4種不同的方式進行了驗證,每次我調用Lock()/ Unlock()時,我的d3d幀的內容都是一個單獨的新幀,所以這裏的問題真的讓D3DImage在渲染之前複製緩衝區下一個。對Lock()/ Unlock()的其他調用不起作用; Lock()僅在控件當前正在呈現時阻塞。 – Asik 2013-05-10 17:07:07

相關問題