
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ランキング
-
別スレッドのデータを受信できない
-
WaitForSingleObjectの使い方に...
-
excelvbaでCreateThreadの動作
-
CWnd::OnTimerのスレッドの取得
-
スレッドの終了の仕方
-
VC++スレッドの正しい終了のさ...
-
DirectX 11のConsntanBuffer
-
スレッドにて同一メモリの書き...
-
C# スレッド終了の監視について
-
C#でスレッド実行中のイベント...
-
マルチスレッドのスレッド数を...
-
VB2005 シリアル通信のClose処理
-
Windows上で、シグナル(SIGTERM...
-
クラスでスレッド作成
-
複数スレッドを動作させるのに...
-
.netアプリへのSendMessageでフ...
-
マルチスレッドプログラミング...
-
ユーザーインターフェイスの一...
-
マルチスレッドでブレイクポイ...
-
スレッドの監視方法
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
スレッドの監視方法について
-
VC++スレッドの正しい終了のさ...
-
スレッドの終了の仕方
-
WaitForSingleObjectの使い方に...
-
Windows上で、シグナル(SIGTERM...
-
別スレッドのデータを受信できない
-
スレッドの安全な終了のさせ方
-
スレッドにて同一メモリの書き...
-
別スレッドからメインダイアロ...
-
マルチスレッドのスレッド数を...
-
C# スレッド終了の監視について
-
MFC通信プログラムマルチスレッ...
-
Linuxでスレッド優先度って変え...
-
メインダイアログが最背面に表...
-
スレッドの終了はどうやるんで...
-
CWnd::OnTimerのスレッドの取得
-
C# スレッドから親ウィンドウへ...
-
メインスレッドのPostMessageと...
-
msec単位のWait Timerが作れない!
-
.NetのBackgroundWorkerクラス...
おすすめ情報