Jump to content
  • Advertisement
Sign in to follow this  
67rtyus

Is Direct3D threading safe? - Locking a resource in another thread

This topic is 3775 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi; I am developing a GUI system for my strategy game at the moment. The GUI system must be informed about mouse actions while the game is running. For example, if the mouse cursor moves on a button, it must enter a "mouse over" state and should change its color accordingly. Normally, I have a game loop (a "while(true)" ) in the main thread, which checks for the Windows messages first, using the PeekMessage, TranslateMessage and DispatchMessage calls. After that, it calls the Render function and this makes the usual D3D calls like SetFVF,SetTexture,DrawIndexedPrimitive,etc.. If I would follow the old single threaded approach here, I am worried that the frequent WM_MOUSEMOVE messages will hold the CPU busy most of time. For every WM_MOUSEMOVE message, the whole GUI tree consisting of various components like windows, buttons, etc. must be checked whether the cursor is on them or not. Traversing on a tree structure involves recursive function calls and doing this operation every time for every mouse move could cause the main thread to spend too much time on it, leaving less CPU time for the critical Render function. My plan is, whenever a mouse event occurs (movement,pressed,clicked...) unblocking a suspended event handler thread to check if the mouse has somehow interacted with the GUI. Most of the CPU's have multiple cores nowadays and this thread can run on another core without interrupting the main renderer thread. If a GUI button enters the "mouse over" state in this event handler thread, I must change the vertex colors of the button. For that I need a Lock operation on the vertex buffer of the GUI.(the vertices and indices of all GUI components reside in batch vertex and index buffers, with the aim of minimizing DrawIndexedPrimitive calls) Now if I call the Lock operation in this event thread,the main thread could be calling any other Direct3D function at the same time. This means different function calls to the Direct3D runtime at the same time from different threads. Is the Direct3D safe for this kind of threading operations? Could this approach cause a deadlock or another synchronization problem? And what are your suggestions for my situation? Thanks in advance.

Share this post


Link to post
Share on other sites
Advertisement
The D3D device isn't thread-safe unless you pass the D3DCREATE_MULTITHREADED flag to IDirect3D9::CreateDevice. If you do pass that, the device will use locks on nearly every operation which will cause noticable overhead. It's recommended that you handle locking and synchronization yourself, at least if performance is an issue.

Share this post


Link to post
Share on other sites
Ok,I don't use that flag, but what about making a Lock call in a thread,other than the main one(without using the D3DCREATE_MULTITHREADED flag)? As far as I know, when we make a D3D call, this call is recorded to a command buffer managed by the driver and then the driver sends those commands to the hardware.(When it sends them, I don't know. However the "Present" function seems a logical place to do it.) I think, that a Lock call from another thread will end up in the same command buffer of the driver, independent of the thread from which it was called. So, it doesn't seem to be a harmful operation to me. What are your thoughts on that?

Share this post


Link to post
Share on other sites
Quote:
Original post by 67rtyus
Ok,I don't use that flag, but what about making a Lock call in a thread,other than the main one(without using the D3DCREATE_MULTITHREADED flag)? As far as I know, when we make a D3D call, this call is recorded to a command buffer managed by the driver and then the driver sends those commands to the hardware.(When it sends them, I don't know. However the "Present" function seems a logical place to do it.) I think, that a Lock call from another thread will end up in the same command buffer of the driver, independent of the thread from which it was called. So, it doesn't seem to be a harmful operation to me. What are your thoughts on that?
Doing anything with D3D from multiple threads without the D3DCREATE_MULTITHREADED is undefined behaviour. The Debug Runtimes will scream and shout at you for accessing D3D resources from multiple threads without the flag.

If you must do this, then lock the resource in the main thread, then pass the locked pointer to a worker thread. Once that's complete, signal the main thread and get it to Unlock() the resource.

If you lock the resource from a second thread, D3D might be accessing it to render it or upload it to the graphics card. In the best case, that'll crash and in the worst case, it'll give you weird undefined behaviour that'll be extremely hard to track down.
This could also give totally different behaviour on different OS's, D3D versions or drivers.

Share this post


Link to post
Share on other sites
Quote:
Original post by 67rtyus
If I would follow the old single threaded approach here, I am worried that the frequent WM_MOUSEMOVE messages will hold the CPU busy most of time.


Just update the state of the GUI in another thread, but make actual modifications to the 3D objects in the main thread. Like:

//GUI thread
button.Color = HoverColor;

//Main thread
vertexBuffer->Lock();
...
v->Color = button.Color;
...
gui->Draw();



Share this post


Link to post
Share on other sites
Quote:
Original post by MJP
The D3D device isn't thread-safe unless you pass the D3DCREATE_MULTITHREADED flag to IDirect3D9::CreateDevice. If you do pass that, the device will use locks on nearly every operation which will cause noticable overhead. It's recommended that you handle locking and synchronization yourself, at least if performance is an issue.


The D3DCREATE_MULTITHREADED doesn't make it much more threadsafe if you're locking/destroying a texture or a vb (etc.) that another thread is using at the same time to draw.

The important is not necessarily the "thread" that calls into d3d (except for createdevice, testforcooperativelevel, reset, see my small article about running d3d9 devices in a multithreaded program ), but more that the "flow" of operations is serialized from the point of view of the device and that means clear design with critical sections, signals between concurrent threads (or leaving the responsibility of calling into the device to one single thread).

For the initial problem (MOUSEMOVE being called too often) why not simply update the mouse position variable during the message and traverse the tree only when you actually need that information (when you're refreshing the display or when you detected a mouse click). Also maybe you can optimize that tree traversal (index elements in a hash table per screen quadrants/tiles, reduce the size of the tree by only storing stuff that reacts to mouse over etc.)

LeGreg

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!