by the Codermind team. |
Question
«In order to take advantage of our multicore processors we're trying to adapt our existing Direct3D code to run multithreaded. Now that worked mostly except that we're getting exceptions sometime when presenting or going full screen. Is Direct3D compatible with multithreading at all ? Should we use the D3DCREATE_MULTITHREAD flag when creating the device ?»
Answer
You can indeed use multithreading and you're not obliged to use the flag multithreaded as long as you're careful enough.
You can follow these simple steps :
Thread 1 (let's call it device creation thread) :
- will call CreateWindow, will poll for messages for that window, will handle WM_XXX messages in place (and message other threads if necessary).
- will call Direct3DCreate, CreateDevice, TestCooperativeLevel, Reset, pDevice->Release and pDirect3D->Release().
Thread 2 (let's call it drawing thread)
- will call DrawPrimitive, Clear, SetRenderstate, LockRect, and all regular rendering calls.
- will call Present, getData.
You will need a good synchro mechanism no matter what (thread safe doesn't mean "thread ignorant"), so here are some sensible measures :
Device creation thread 1, should only call one of createDevice, TestCooperativeLevel, Reset and pDevice->Release when it has made sure that drawing Thread 2 is not busy. That would mean that drawing thread should be able to soldier on, or go to the next synchronization post if there was an error like device lost. If there was an error like device lost when presenting for example then drawing thread 2 would signal it to device creation thread 1, so that thread 1 would call TestCooperativeLevel() and ultimately Reset(). The Drawing thread 2 can go to sleep after signaling the error until after the Reset.
Both the device creation thread 1 and the drawing thread 2 can call CreateTexture, CreateRendertarget, CreateQuery, CreateStateBlock etc.. As long as both threads don't call into the device concurrently. Same with Release(). Of course Device creation thread 1 should make sure before calling Reset() and pDevice->Release(), that all device objects are correctly released. Since Device Creation thread 1 can initiate a Reset and device release by itself, it may make sense that it should call the Release on allocated objects itself in order to limit the number of back and forth between threads.
How about the flag D3DCREATE_MULTITHREAD ? You should not need it with the above scheme since you implicitely ensured that all calls to the device are serialized. But youll probably realize it's a good idea to set it when running with the debug runtime, because the debug runtime will print endlessly that it is being accessed by multiple threads. Release runtime shouldn't care.
Note that the thread 1 has a strong requirement. All calls should be made from the same thread ID. That's the reason why createWindow and createDevice are in the same thread. Thread 2 calls on the other hand can be made from various thread IDs, that way you can more easily use pools of thread for rendering. Of course even if you use several thread IDs, you still need to ensure that no more than one thread calls into the device at one time.




