DirectX 11 ConsntanBufferの更新について
マルチスレッドでConsntanBufferの更新を行うときにエラーが出てしまいます。
基本的な設計ですが、
1. メインのシーケンスを行うスレッド
2.DeviceContextに描画コマンドを詰むスレッド
のスレッド2本構成です。
1から2へのスレッドへは仮想的な描画コマンドを発行し、
ダブルバッファリングを行い並列性を高めています。
# よくある方式だと思います。
HLSLで簡単なシェーダを作りポリゴンを表示することはできたのですが、
DirectXをDebugモードで実行すると以下のようなエラーが出てしまいました。
D3D11: CORRUPTION: ID3D11DeviceContext::Map: Two threads were found to be executing functions associated with the same Device at the same time. This will cause corruption of memory. Appropriate thread synchronization needs to occur external to the Direct3D API. 3516 and 5376 are the implicated thread ids. [ MISCELLANEOUS CORRUPTION #28: CORRUPTED_MULTITHREADING ]
内容はスレッド間で同じバッファに対してID3D11DeviceContext::Mapを実行しているため、
中身が破壊される可能性があるというものです。
先述の通り、基本的にDeviceContextを取り扱うのは2のスレッドです。
しかし、内容が動的に変更されるバッファについては
1のスレッドでID3D11DeviceContext::Mapを実行し、
取得したアドレスを直接書き換えるようにしています。
エラーではID3D11DeviceContext::Mapが2つのスレッドで実行されているというのですが、
初期化時は別として、通常のアップデート処理を行っている間は1のスレッドでしか使っていません。
書き込み先のバッファもダブルバッファ化しており、
またCPUAccess / USAGE / BIND / MAPのフラグも
D3D11_CPU_ACCESS_WRITE / D3D11_USAGE_DYNAMIC / D3D11_BIND_CONSTANT_BUFFER / D3D11_MAP_WRITE_DISCARD
と、動的バッファを使うときの標準的な組み合わせで使用しています。
いろいろとサンプルプログラムを見ているのですが、
出回っているのは大抵がシングルスレッドで作られており、
今回の件に関してはあまり参考になりませんでした。
プログラム中ID3D11DeviceContext::Mapを呼び出しているところは
かなり限定できるため、おそらく間違いないありません。
いろいろ調べては見たのですがエラーが出てしまう原因が特定できずに困っています。
エラーの解決方法をご存知の方はいらっしゃいますでしょうか?
■補則
D3D11_MAP_WRITE_DISCARDはCPUとGPUの同期を制御するフラグですので、
CPUとCPUの競合がおきている今回の件とは別のはずです。
他にはID3D11DeviceContextのメソッド中の何かが
内部でID3D11DeviceContext::Mapを呼び出している可能性がありそうです。
2のスレッドで問題のバッファにアクセスしているメソッドは
ID3D11DeviceContext::VSSetConstantBuffersぐらいです。
(これが内部でMapを呼び出しているとは思えませんが…)
No.1ベストアンサー
- 回答日時:
デバイス コンテキストID3D11DeviceContextのメソッドはいずれもスレッド セーフではありません。
呼び出しがスレッド セーフになったのはID3D11Deviceだけです。そのため、デバイス コンテキストはスレッドごとに作成する必要があります。
具体的には、メインスレッドではイミディエイト コンテキスト(Immediate Context)、サブスレッドではディファード コンテキスト(Deferred Context)として、それぞれスレッドごとに作成する必要があります。
Direct3D 11のマルチスレッド レンダリングは、サブスレッド側で遅延された描画コマンドのリストを記録(作成)し、メインスレッド側で再生(実行)する、という方式になっています。
DirectX SDKにMultithreadedRendering11サンプルが付いているので、(例のごとくDXUTやら大量のグローバル変数やらで少し読みづらいですが)それが参考になるかと思います。Map/Unmapは各スレッドごとに独立して実行しています。
http://msdn.microsoft.com/ja-jp/library/ee422102 …
http://wlog.flatlib.jp/archive/1/2008-11-7
この回答への補足
>デバイス コンテキストID3D11DeviceContextのメソッドはいずれもスレッド セーフではありません。
>呼び出しがスレッド セーフになったのはID3D11Deviceだけです。そのため、デバイス コンテキストはスレッドごとに作成する必要があります。
なるほど。Direct3D 9ではMULTITHREADオプションでスレッドセーフに出来ましたが、
おっしゃるとおりスレッド単位でコンテキストを分けた方が
クリティカルセクションのロスが減らせそうです。
DeferredContextはDirect 3D 10では使えないようですね。
今作っているプログラムはマルチプラットホーム対応を検討しているため、
できるだけ標準化されたAPIで実装しようとしています。
となればリソースの更新も仮想コマンドを投げて
2のスレッドで行うことにします。
ただ、この方法ですと書き込み先のアドレスが、
書き込み元のスレッドで取得できなくなります。
1のスレッドではテンポラリバッファを用意し、
そこに書き込んだ後、2のスレッドでMapをしmemcpyをします。
メモリが余分に必要なのに加えてmemcpy分の処理が上積みされてしまいます。
残るは毎フレームID3D11Device::CreateBuffer/ID3D11Buffer::Releaseを呼び出す方法です。
D3D11_SUBRESOURCE_DATAに1のスレッドで操作しているバッファをセットするので、
コピー処理は不要です。
# Direct3D 9ではコピーが必須ですし、
# Direct3D 10以降であっても内部的にコピー処理が走るかも知れないので
# 気休めではありますが…
バッファの生成と破棄が大量に呼び出されることになりますが
メモリ容量と処理速度と汎用化のバランスが一番とれている気がします。
初期化時にSINGLETHREADフラグがセットできるので、
実質Direct3D 9と同じ扱いになりそうです。
話は脱線しましたが、
ID3D11DeviceContextがスレッドセーフでないことは理解しました。
# もっとわかりやすい警告を出してくれれば速く気づけたのですが…
大変助かりました。
どうもありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- その他(プログラミング・Web制作) pythonのmap、結果の利用は1度だけ? 5 2022/06/11 12:33
- その他(プログラミング・Web制作) Windowsのマクロプログラムで、こんなことできますか? 3 2022/06/28 14:30
- Access(アクセス) アクセス where句を使用して複数条件抽出をするには 2 2022/08/29 13:24
- PHP htmlspecialcharsが機能していないです。 バグですか? 1 2022/04/05 01:22
- MySQL 参考書に従って入力したつもりでしたが、最後はエラーがでました。 1 2022/09/28 03:45
- 英語 Although bleeding does not occur on every occasion 1 2023/03/03 20:10
- PHP ここでの ②if($su_d<>"")の比較演算子 を使う理由は 1 2022/03/26 02:33
- Access(アクセス) アクセス テーブルの空白を変数に置換するボタンが作りたい 4 2022/07/08 11:19
- Google Maps google map経路検索で、進行方向とmapの向きを一致させたい 3 2022/04/10 14:20
- Yahoo!メール YahooIDをパスワード形式にしただけなのに、利用規約違反とされて電話番号が使えなくなりました 3 2023/03/25 04:08
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
スレッドの安全な終了のさせ方
-
C++でマルチスレッドによるネッ...
-
スレッドにて同一メモリの書き...
-
C言語である関数への同時アクセ...
-
アラート可能な待機状態とは
-
スレッドの終了の仕方
-
Macターミナルで実行中のプログ...
-
緯度、経度の 10進法と 60進法...
-
家電製品の電力周波数を変える機械
-
大容量のメモリ確保をスワップ...
-
Mac 乗数の入力方法
-
【C言語 数独】 C言語で9×9の数...
-
符号付きにすべきか、符号なし...
-
Excel-VBAのmsgBox()の不思議
-
VBA
-
変換のプログラムを教えてくだ...
-
tex 郵便記号のだしかた
-
英数文字列のうちの数値を4桁に...
-
ソケット通信の送受信遅延-02 ...
-
スーパーのレジで並んでいたら...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
WaitForSingleObjectの使い方に...
-
VC++スレッドの正しい終了のさ...
-
スレッドの監視方法について
-
スレッドの安全な終了のさせ方
-
スレッドにて同一メモリの書き...
-
Windows上で、シグナル(SIGTERM...
-
スレッドの終了の仕方
-
スレッドの終了はどうやるんで...
-
VB2005 シリアル通信のClose処理
-
マルチスレッドプログラミング...
-
.netアプリへのSendMessageでフ...
-
マルチスレッドについて
-
Linuxでスレッド優先度って変え...
-
Win32APIでのスレッド処理
-
pthread_cond_wait 取りこぼし?
-
LinuxでDoEvents()同等機能
-
C++ GUIのメッセージループ。
-
excelvbaでCreateThreadの動作
-
マルチスレッドの実行順序の制御
-
同一スレッドで、ロックをかけ...
おすすめ情報