2011-02-10 77 views
3

經過許多小時的調試和分析後,我終於設法找出競爭條件的原因。解決它是另一回事!Java AWT drawImage競態條件 - 如何使用同步來避免它

爲了看到實際的競爭狀況,我在調試過程中錄製了一段視頻。我從那時起就進一步瞭解了這種情況,所以請原諒貧窮的評論和作爲調試過程一部分而實施的愚蠢機制。

http://screencast.com/t/aTAk1NOVanjR

所以,這種情況:我們有一個雙緩衝實現的表面(即java.awt.Frame中或窗口),其中有一個正在執行的線程,基本上連續循環,調用渲染進程(執行用戶界面佈局並將其渲染到後臺緩衝區),然後進行後期渲染,將渲染區域從後臺緩衝區傳送到屏幕。

這裏是雙重的僞代碼版本(完整版線Surface.java 824)緩衝呈現:

public RenderedRegions render() { 
    // pseudo code 
    RenderedRegions r = super.render(); 
    if (r==null) // nothing rendered 
     return 
    for (region in r) 
     establish max bounds 
    blit(max bounds) 
    return r; 
} 

與任何AWT表面實現,它還實現(線507 AWT.java - 鏈接限制:( - 使用Surface.java鏈接,與高原/ AWT.java更換核心/ Surface.java)油漆/更新覆蓋這也從位塊傳輸的後備緩衝到屏幕上:

 public void paint(Graphics gr) { 
      Rectangle r = gr.getClipBounds(); 
      refreshFromBackbuffer(r.x - leftInset, r.y - topInset, r.width, r.height); 
     } 

阻擊器實現(使用的drawImage線371 AWT.java)()函數:

/** synchronized as otherwise it is possible to blit before images have been rendered to the backbuffer */ 
    public synchronized void blit(PixelBuffer s, int sx, int sy, int dx, int dy, int dx2, int dy2) { 
     discoverInsets(); 
     try { 
      window.getGraphics().drawImage(((AWTPixelBuffer)s).i, 
           dx + leftInset, dy + topInset,  // destination topleft corner 
           dx2 + leftInset, dy2 + topInset, // destination bottomright corner 
           sx, sy,       // source topleft corner 
           sx + (dx2 - dx), sy + (dy2 - dy), // source bottomright corner 
           null); 
     } catch (NullPointerException npe) { /* FIXME: handle this gracefully */ } 
    } 

(警告:這是我開始變得清晰假設)

這裏的問題似乎是個drawImage是異步的一個!從refreshBackBuffer()通過繪製/更新blit首先被調用,但出現秒。

所以... blit已經同步。防止競爭狀況的顯而易見的方式不起作用。 :(

到目前爲止,我已經提出了兩個解決方案,但他們都不是理想的:在接下來的

  1. 重塊傳送渲染過程
    缺點:性能損失,仍然得到了一點閃爍由於遇到競爭狀態(有效屏幕 - >無效屏幕 - >有效屏幕)時

  2. 不要的blit油漆/更新,而不是設置刷新範圍,並使用這些邊界下一個渲染通道
    缺點:讓黑當屏幕無效時閃爍主要應用程序線程正在追趕

這裏(1)似乎是兩個邪惡中較小的一個。 編輯:和(2)不起作用,獲得空白屏幕......(1)工作正常,但只是掩蓋了問題,可能仍然存在。

由於我對同步以及如何使用它的理解不夠,我似乎無法想象它是一種鎖定機制,它以某種方式說明了drawImage()的異步特性。

或者也許使用ImageObserver?

注意的是,由於應用程序的性質(Vexi,對於那些有興趣,網站是過時的,我只能用2個超鏈接)渲染線程必須是油漆/更新之外 - 它有一個單線程腳本模型和佈局過程(渲染的子流程)調用腳本。

回答

0

更新:好辦法在這裏:AWT custom rendering - capture smooth resizes and eliminate resize flicker


的答案在這裏是爲了除去從paint()線程中的所有塊傳輸,即永遠只能從程序線程後備緩衝刷新。這與Jochen Bedersdorfer提出的答案是相反的,但他的回答對我們來說永遠不會起作用,因爲該程序有自己的腳本模型,它與驅動渲染的佈局模型集成在一起,因此它們都必須按順序發生。 (推測)一些問題源於Java中帶有加速圖形芯片組的不太好的多顯示器支持,因爲我在適應使用BufferStrategy時遇到了this problem,這是一種direct3d + Java差異。

實質上paint()update()被簡化爲阻止呼叫。這樣做效果更好,但有一個缺點 - 沒有平滑調整大小。

private class InnerFrame extends Frame() { 
    public void update(Graphics g) { } 
    public void paint(Graphics g) { } 
    .... 
} 

我最終使用的緩衝策略,雖然我不是100%滿意這個方法,因爲它在我看來,這是低效的被渲染的圖像,然後複製完整的圖像到BufferStrategy中和然後執行show()進行屏幕。

我也實現了一個基於Swing的替代方法,但我不再特別喜歡這個。它使用帶有ImageIcon的JLabel,由此程序線程(不是EDT)繪製到由ImageIcon包裝的圖像。

我敢肯定有一個後續的問題,我要問,當我有更多的時間來研究這個更多的目的,但現在我有兩個工作的實施,隨着貼在這裏或多或少地址初始困境 - 我學到了很多發現他們的故事。

0

不確定,但是如果您在AWT繪製線程中Blit,會發生什麼?

+0

我假設你的意思是'發生了什麼 - 問題仍然存在。我只是嘗試將blit代碼複製到paint方法中,並且其行爲相同。 (FWIW它並沒有真正的區別,因爲paint已經通過refreshFromBackBuffer間接地調用了blit,但爲了徹底,我嘗試了你的建議。) – 2011-02-10 01:50:24