2015-10-15 73 views
5

私はWindowsストア用の電子ブックリーダーアプリケーションを作成しています。私はDirect2D + DXGIのスワップチェーンを使って画面上のブックページをレンダリングしています。効率的なDirect2Dマルチスレッド

私の書籍のコンテンツは時には非常に複雑(ジオメトリ、ビットマップ、マスクなど)なので、レンダリングには最大100ミリ秒かかることがあります。だから、別のスレッドでビットマップにオフスクリーンレンダリングを実行しようとしているし、メインスレッドでこのビットマップを表示するだけです。

しかし、私はそれを効率的に行う方法を理解することはできません。

は、これまでのところ私は2つのアプローチを試してみた:

  1. は、D2D1_FACTORY_TYPE_MULTI_THREADEDフラグで単一ID2D1Factoryを使用ID2D1BitmapRenderTargetを作成し、オフスクリーンレンダリングのためのバックグラウンドスレッドでそれを使用しています。 (これにはさらに、IDXGISwapChain::Presentの操作でID2D1Multithread::Enter/Leaveが必要です)。問題は、バックグラウンドスレッドでID2D1RenderTarget::EndDrawの操作が最大100msかかることがあり、内部Direct2Dロッキングのためにメインスレッドレンダリングがこの期間中ブロックされることです。

  2. バックグラウンドスレッドで(http://www.sdknews.com/ios/using-direct2d-for-server-side-renderingに記載されているように)個別のID2D1Factoryを使用し、内部Direct2D同期をオフにします。この場合、2つのスレッド間にはクロスロックはありません。残念ながら、このケースでは、別のファクトリに属しているため、メインビットID2D1Factoryで結果のビットマップを直接使用することはできません。ビットマップデータをCPUメモリに移動してから、メインのID2D1FactoryのGPUメモリにコピーする必要があります。この操作でも大きな遅延が発生します(大きなメモリアクセスが原因と思われますが、わかりません)。

これを効率的に行う方法はありますか?

P.S.ここのタイミングはすべてAcer Switch 10タブレットに与えられています。通常のCore i7 PCでは、両方のアプローチが目に見える遅れなく動作します。

答えて

4

私は解決策を見つけました。

基本的には、2つのDirectXファクトリセット間のDXGIリソース共有を使用するようにアプローチ2を変更するだけでした。私は(彼らはここで見つけることができます:http://xboxforums.create.msdn.com/forums/t/66208.aspxを)すべての血みどろの詳細を省略しますが、基本的な手順は次のとおりです。

  1. は、DirectXのリソースの2セットを作成します。メイン(画面上のレンダリングに使用される)、および二次的に(オフスクリーンレンダリングの場合)。メインリソースセットからID3D11Device2を使用
  2. CreateTexture2DD3D11_BIND_RENDER_TARGETD3D11_BIND_SHADER_RESOURCED3D11_RESOURCE_MISC_SHARED_NTHANDLED3D11_RESOURCE_MISC_SHARED_KEYEDMUTEXフラグでD3D 2Dテクスチャを作成します。
  3. IDXGIResource1にキャストしてXGI_SHARED_RESOURCE_READDXGI_SHARED_RESOURCE_WRITECreateSharedHandleを呼び出して、共有ハンドルを取得します。
  4. ID3D11Device2::OpenSharedResource1を呼び出して、この共有テクスチャをバックグラウンドスレッドのセカンダリリソースセットで開きます。
  5. このテクスチャのキー付きミューテックス(IDXGIKeyedMutex::AcquireSync)を取得し、そこからレンダリングターゲットを作成し(ID2D1Factory2::CreateDxgiSurfaceRenderTarget)、描画し、ミューテックス(IDXGIKeyedMutex::ReleaseSync)をリリースします。
  6. メインスレッドでは、メインリソースセットで、mutexを取得し、手順2で作成したテクスチャから共有ビットマップを作成し、このビットマップを描画してからmutexを解放します。

ミューテックスロックスタッフが必要です。それを行わないと、DirectXのデバッグエラーメッセージが誤って表示されたり、誤った操作が行われたり、クラッシュすることさえあります。

0

tl; dr:ソフトウェアモードでバックグラウンドスレッドのビットマップにレンダリングします。ハードウェアモードでUIスレッドのターゲットをレンダリングするためのビットマップから描画します。

私がこれまでに見つけることができました最善のアプローチはソフトウェアレンダリング(IWICImagingFactory::CreateBitmapID2D1Factory::CreateWicBitmapRenderTarget)でバックグラウンドスレッドを使用して、ID2D1RenderTarget::CreateBitmapFromWicBitmap経由でレンダーターゲットハードウェアに戻っスレッド上のハードウェアのビットマップにコピーすることです。次に、ID2D1RenderTarget::DrawBitmapを使ってblitします。

paint.net 4.0が選択レンダリングを行う方法です。 lassoツールを使用して選択範囲を描画する場合、バックグラウンドスレッドを使用して選択範囲のアウトラインを非同期に描画します(UIスレッドはこれを完了するまで待機しません)。ストロークスタイルやアニメーションのため、非常に複雑なポリゴンになることがあります。私はそれを4回レンダリングします。ここで、各アニメーションフレームは破線のストロークスタイルに対してわずかに異なるオフセットを持ちます。

明らかに、このレンダリングにはポリゴンが複雑になるほど(つまり、しばらく落書きを続けている場合)時間がかかることがあります。背景のスレッドが新しい変換で現在のポリゴンをまだ再レンダリングしていない場合は、私はあなたが変換(回転、平行移動、スケール)を行うことを可能にするMove Selectionツールを使用するときに、新しい変換が適用された古いビットマップ(現在のポリゴンと古い変換あり)がレンダリングされます。バックグラウンドスレッドがキャッチアップしている間に、選択アウトラインが歪んでいる(スケーリングされている)か、切り抜かれている(表示可能領域の外に移動した)が、60fpsの応答性を支払うのにわずかな代償です。ポリゴンの変形を同時に変更することはできないため、この最適化は非常にうまく機能します。

関連する問題