デバッグと分析に何時間も費やした後、競合状態の原因を特定することができました。それを解決することは別の問題です!
競合状態の動作を確認するために、デバッグ プロセスの途中でビデオを録画しました。それ以来、私は状況の理解を深めてきたので、貧弱なコメントとデバッグ プロセスの一部として実装されたばかげたメカニズムを許してください。
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 つの悪の小さい方のようです。 編集: (2) 機能せず、空白の画面が表示されます... (1) 正常に機能しますが、潜在的にまだ存在する問題を隠しているだけです。
私が望んでいるのは、同期とその使用方法についての私の理解が不十分なために思い浮かびそうにないことですが、drawImage() の非同期の性質をどうにかして説明するロック メカニズムです。
それとも、ImageObserver を使用しますか?
アプリケーションの性質上 (Vexi、興味のある方のために、ウェブサイトは古く、2 つのハイパーリンクしか使用できません)、レンダリング スレッドはペイント/更新の外にある必要があることに注意してください。単一スレッドのスクリプト モデルがあり、レイアウト プロセス (レンダリングのサブプロセス) は、スクリプトを呼び出します。