Direct2D を使用する実装の 1 つ、既存のグラフィックス ライブラリを使用しています。(GDI+、Quartz なども使用できます。) Direct2D オブジェクトのラッパー コードとしてすべて実装されたビットマップ オブジェクト、ペン、ブラシなどがあります。ライブラリはクローズド ソースであり、残念ながらここにその大部分を掲載することはおそらくできません。
複数のスレッドを使用する場合 (1 つのスレッド プロデューサーがビットマップを作成してそれらに描画し、1 つのスレッド コンシューマーが画面上に描画する場合)、プロデューサー スレッドが生成するビットマップが完全に空白になることがあります。画像データ (内部的にはテクスチャ) はすべてゼロです。 . (ビットマップはテクスチャのラッパー オブジェクトですが、GDI+ ビットマップ オブジェクトのようにビットマップを表すと、おそらく GDI+ ビットマップ オブジェクトを思い浮かべるかもしれません。一般的なパラダイムに精通していると思います。)決してクラッシュしないことに注意してください。静かに失敗します。典型的な症状は、ビットマップ データへの描画Unmap
、バッファーからテクスチャへのコピーの呼び出し、後で描画しようとしたときにテクスチャが空白であることが判明することです。
私が考えていることは次のとおりです。
- 私のプロデューサ スレッドが行っているように、ピクセル データに直接書き込むために、ライブラリは、ピクセル データに直接アクセスする機能を提供
Map
します。Unmap
「共有デバイス」と呼ばれる DirectX10 デバイスが 1 つあり、これを使用してCopyResourceを呼び出し、内部テクスチャからバッファにコピーします。バッファは自由に書き込むことができます。次に、逆方向にコピーをアンマップします。時々これは静かに失敗すると思います。 - 共有デバイスのこのアプローチは、ライブラリ全体で使用されてい
ID2D1RenderTarget
ます。共有ビットマップ ( ) を作成するために使用される共有レンダー ターゲット ( )ID2D1Bitmap
などがあります。このアプローチは、もちろん、グラフィックス/ビットマップ オブジェクトが独自のテクスチャを持っているように見えますが、すべてそれらに対する操作は、1 つのデバイス、1 つのレンダー ターゲットなどで行われます。 - 複数のスレッドが同時に「ビットマップ」を使用すると、いくつかの操作が失敗する可能性があり
CopyResource
ます。また、静かに失敗します。void
HRESULT
ID2D1RenderTarget::DrawBitmap
そのため、問題を解決し、プロデューサー/コンシューマー スレッド システムを確実に実現できる十分なスレッド セーフをライブラリにハックしたいと考えています。どうすればいいですか?
私は、複数のスレッドから Direct2D を使用する正しい方法について多くのことを読んできました。
すべてのファクトリは
D2D1_FACTORY_TYPE_MULTI_THREADED
フラグですでに作成されています。factory 経由で同期するための
ID2D1MultiThread
インターフェイスを使用してみました。ただし、これは Windows 7 以降でしか利用できないようです。DirectX10 レベルの Vista+ API を使用する必要があります。また、同期のためにID3D10Multithread インターフェイスを使用してみました。私はこれでいくつかの問題に遭遇しました:
どこで同期する必要があるのか 、または何を同期する必要があるのか わかりません。
Map
アラウンドアンドUnmap
コール、またはMSDN が示すようにアラウンドが必要であるなど、きめ細かいものである場合Present
、同期は効果がありません。上記のメソッド呼び出しは依然として失敗します。それがより粗い場合、ID2D1RenderTarget::BeginDrawを呼び出すときにロックに入り、すべての描画のためにそれを保持し、ID2D1RenderTarget::EndDraw
、ID2D1RenderTarget::Flush
および/またはIDXGISwapChain::Present
が呼び出された後にロックを離れると、見事に機能するようです...数秒後に入力するまでデッドロック。MSDNはこれを説明しています、フルスクリーン スワップ チェーンを使用する場合は、メッセージ ポンプ スレッドがレンダリング スレッドで待機しないように注意してください。たとえば、(レンダリング スレッドから) IDXGISwapChain1::Present1 を呼び出すと、レンダリング スレッドがメッセージ ポンプ スレッドで待機する場合があります。モード変更が発生すると、Present1 が ::SetWindowPos() または ::SetWindowStyle() を呼び出し、これらのメソッドのいずれかが ::SendMessage() を呼び出す場合に、このシナリオが可能になります。このシナリオでは、メッセージ ポンプ スレッドにそれを保護するクリティカル セクションがある場合、またはレンダリング スレッドがブロックされている場合、2 つのスレッドはデッドロックします。-ソース
...しかし、それを回避する方法についてのガイダンスはありません。デッドロックは、2 つのスレッドが同時にグラフィックスにアクセスしようとしていることが原因のようです。たとえば
BeginDraw
、 を同時に呼び出しています。2 番目のロックが動作しているように見えます。ロックが 1 つだけあればデッドロックは発生しないからです。「共有」デバイス、レンダー ターゲットなどのスレッドごとのインスタンスを維持しようとしました。つまり、各タイプのオブジェクトのスレッドごとに 1 つのインスタンスがあり、そのスレッドで実行されるすべてのコードで使用されます。これは、テクスチャを除いてうまく機能します。別のスレッド コンテキストで (別のデバイスによって) 作成されたテクスチャへのアクセスはまったく機能しないようです。この手法をコーディングしているときに、メソッドが失敗する問題によく遭遇しました。最も一般的なのは
CreateSharedBitmap
で失敗するD2DERR_UNSUPPORTED_OPERATION
もので、テクスチャが別のスレッド (つまり、別のデバイス) から取得されたものです。これが重要なビットです。あるスレッドで作成されたテクスチャ) は、別のスレッドで描画できる必要があります。コーディング方法に応じて、スレッドとデバイス、レンダー ターゲットなどの間に 1:1 の関係がある場合とない場合があります。これを解決できれば、コーディングした残りの部分は、必要なことを達成するのに十分に機能すると思います。それは可能ですか?
ID3D10Texture2D
- s の間でsを交差するにはID3D10Device1
? スレッド間で個別のデバイスを用意する必要がありますか?
全体として、私は少し困惑しており、Direct3D 10 と Direct2D に精通している方から、最善のアプローチについてアドバイスを求めています。次の 2 つの実行可能な可能性があると思います。
ID3D10Multithreadインターフェイスのロックを把握します。Google にはあまりありませんし、MSDN のデッドロックを回避するためのアドバイスもあまりありません。
すべてのスレッドが独自のデバイス、共有レンダー ターゲットなどを持つメカニズムを続行します。あるスレッドから別のスレッドにテクスチャ リソースをどのようにクロスさせることができますか? 基本的に、1 つのスレッドで内部的に作成されたテクスチャを使用するラッパー ビットマップ オブジェクトは、2 番目に描画するために使用できる必要があります。
しかし、他の可能性についても聞きたいと思っています。正しい Direct2D スレッド化の実装に関するアドバイスは、非常にありがたいものです。