3

デバッグと分析に何時間も費やした後、競合状態の原因を特定することができました。それを解決することは別の問題です!

競合状態の動作を確認するために、デバッグ プロセスの途中でビデオを録画しました。それ以来、私は状況の理解を深めてきたので、貧弱なコメントとデバッグ プロセスの一部として実装されたばかげたメカニズムを許してください。

http://screencast.com/t/aTAk1NOVanjR

したがって、状況: サーフェス (つまり、java.awt.Frame または Window) のダブル バッファー実装があり、実質的に継続的にループし、レンダリング プロセス (UI レイアウトを実行してバックバッファーにレンダリングする) を呼び出す進行中のスレッドがあります。 ) そして、レンダリング後、レンダリングされた領域をバックバッファからスクリーンにブリットします。

ダブル バッファリングされたレンダリングの疑似コード バージョン ( 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 サーフェス実装と同様に、(AWT.java の 507 行目 -リンク制限 :( - Surface.java リンクを使用し、core/Surface.java を plat/AWT.java に置き換えます) ペイント/更新オーバーライドも実装します。バックバッファからスクリーンへ:

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

ブリッティングは drawImage() 関数を使用して実装されています (AWT.java の 371 行目)。

    /** 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() からのブリットが最初に呼び出され、2 番目に発生することです。

つまり... blit は既に同期されています。競合状態を防ぐ明白な方法は機能しません。:(

これまでのところ、2 つの解決策を思いつきましたが、どちらも理想的ではありません。

  1. 次のレンダー パスで再ブリットする
    短所: パフォーマンス ヒット、競合状態 (有効な画面 -> 無効な画面 -> 有効な画面) が発生したときにちらつきが発生する

  2. ペイント/更新時にブリットしないでください。代わりに、リフレッシュ境界を設定し、次のレンダー パスでそれらの境界を使用します

ここで (1) は 2 つの悪の小さい方のようです。 編集: (2) 機能せず、空白の画面が表示されます... (1) 正常に機能しますが、潜在的にまだ存在する問題を隠しているだけです。

私が望んでいるのは、同期とその使用方法についての私の理解が不十分なために思い浮かびそうにないことですが、drawImage() の非同期の性質をどうにかして説明するロック メカニズムです。

それとも、ImageObserver を使用しますか?

アプリケーションの性質上 (Vexi、興味のある方のために、ウェブサイトは古く、2 つのハイパーリンクしか使用できません)、レンダリング スレッドはペイント/更新の外にある必要があることに注意してください。単一スレッドのスクリプト モデルがあり、レイアウト プロセス (レンダリングのサブプロセス) は、スクリプトを呼び出します。

4

2 に答える 2

1

更新:ここでの良いアプローチ: AWT カスタム レンダリング - スムーズなサイズ変更をキャプチャし、サイズ変更のちらつきをなくす


ここでの答えは、paint()スレッドからすべてのブリッティングを削除することでした。つまり、プログラム スレッドのバックバッファからのみ更新するようにしました。これは Jochen Bedersdorfer が提案した答えとは逆ですが、プログラムにはレンダリングを駆動するレイアウト モデルと統合された独自のスクリプト モデルがあるため、彼の答えは決してうまくいきませんでした。

(憶測) いくつかの問題は、高速化されたグラフィックス チップセットを使用した Java でのマルチ モニターのサポートが優れていないことに起因します。これは、Direct3d + Java の不一致である BufferStrategy の使用に適応するときにこの問題に遭遇したためです。

基本的paint()に、update()ブロッキング呼び出しに削減されます。これははるかにうまく機能しますが、1 つの欠点があります。サイズ変更がスムーズに行われないことです。

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

バッファ戦略を使用することになりましたが、このアプローチには 100% 満足していません。画像にレンダリングしてから、完全な画像を BufferStrategy にコピーしてshow()から to screen を実行するのは効率が悪いと思われるからです。

Swing ベースの代替手段も実装しましたが、これも特に好きではありません。JLabel と ImageIcon を使用することで、プログラム スレッド (EDT ではない) が ImageIcon によってラップされた Image を描画します。

より多くの目的を持ってこれを調べるための時間ができたら、フォローアップの質問があると確信していますが、今のところ、ここに投稿されているように、最初の問題に多かれ少なかれ対処する2つの実用的な実装があります-そして私は学びましたそれらを発見するヘルバロット。

于 2011-07-24T23:40:00.847 に答える
0

わかりませんが、AWT ペイント スレッドで Blit を実行するとどうなりますか?

于 2011-02-10T01:42:00.893 に答える