Resource loading and multithreading

Started by
9 comments, last by Racky1275 16 years, 8 months ago
I'm not sure that this is the correct place for this thread but as it also involves DirectX I'll put it here. I currently have a problem when I load resources in my game. I have a lot of resources and also do some processing on them and all this takes time (20s on an average computer). I display a simple loading animation while this is happening and it gets updated by update calls in between different sections of the loading process. This all work fine. The problem is that as this is a single-threaded app the loading routine is causing the app to be unresponsive, as it's not processing any window messages. This was somewhat fine, until Vista. Vista is very quick to judge my game as unresponsive and the client area of the window goes blank, until the loading is finished. So I thought this was a good time to fix this problem, but I don't know how. I'm thinking about separating the message pump and the rest of the game into two threads. But is it possible to put D3D and the rest of DirectX into a separate thread? As far as I know the window message pump must be in the main thread, right? After a lot of reading I have found conflicting information about using D3D in anything but the main thread aswell. What I want to know is, is it possible to run D3D and the rest in another thread than the main one? And is this a good approach or is there a better or easier way. What I want is the be able to process window messages while loading resources, where some (most) of those are for D3D.
Advertisement
It is told that using DirectX from more than one thread in parallel leads to havoc... even if the MULTITHREADED flag is set upon device creation. It is ok to access D3D from a different thread than the main thread, but due to the docs some critical functions like Device::Reset() have to be called from the main thread.

I have no personal experience on that topic, though. Brushing it up at the moment because I'm about to push resource loading to a parallel thread and I want to put the engine render pipeline at a second CPU core. Looks non-trivial.

Bye, Thomas
----------
Gonna try that "Indie" stuff I keep hearing about. Let's start with Splatter.
It shouldn't be a problem to run D3D in worker threads, Managed DX can do it just fine.

But, if you are concerned about it, why not just "invert" your program? As in, run the UI in the main thread, and the resource loading in a worker thread. Should give the same result.
Yes, I read about the problem, using D3D from several threads, too. But in my case, all use of D3D will be from 1 thread only, just not the main one. I wonder if D3D has a problem with that too?
As long as you create your Device (and DX interface?) on that single other thread as well, this should work out fine. Just be sure to test on a dual core machine, even if you plan to only use D3D from a single thread. It's easy enough to have another thread start messing with D3D by accident, for me at least [smile]
Rim van Wersch [ MDXInfo ] [ XNAInfo ] [ YouTube ] - Do yourself a favor and bookmark this excellent free online D3D/shader book!
Just remember this:
Quote:For these reasons, Direct3D is designed so that the methods IDirect3DDevice9::Reset, IDirect3D9::CreateDevice, IDirect3DDevice9::TestCooperativeLevel, or the final Release of IDirect3DDevice9 can only be called from the same thread that handles window messages.
Ok, hope this isn't straying too far off topic...

Without using the slow MULTITHREADED flag, is it 'safe' to use different threads for different instances of D3D where:

1) Each thread would target a unique gfx card. (maybe 2 cards in PC)
2) Each thread would issue it's own create/reset etc, in fullscreen mode.

Should all threads share the same focus window?
Evil Steve: yeah, that's the part in the doc that made me wonder if my idea really could work.

If it has to be on the same thread that handles window messages then how can I make the program responsive while loading resources and creating textures and such. The only option seems to be to somehow interrupt the resource loading to handle messages, but that could become very tricky. Especially with Vista beeing so fast to mark a program as "not responding".
It's just these four functions that you have to call from the main thread. All drawing and presenting can still be offloaded to a different thread, as long as this thread is the only one calling D3D functions while it lasts.
----------
Gonna try that "Indie" stuff I keep hearing about. Let's start with Splatter.
Quote:This was somewhat fine, until Vista. Vista is very quick to judge my game as unresponsive and the client area of the window goes blank, until the loading is finished.
Maybe vista is more twitchy, but I've had several apps over the years show exactly the same symptoms on WinXP.

I solved it once by just iterating the message loop each time the progress bar was updated. Was a bit fugly at the code level but it did the job.

Quote:Original post by Racky1275
Without using the slow MULTITHREADED flag, is it 'safe' to use different threads for different instances of D3D where:

1) Each thread would target a unique gfx card. (maybe 2 cards in PC)
2) Each thread would issue it's own create/reset etc, in fullscreen mode.

Should all threads share the same focus window?
If you're creating seperate devices then you should be fine - the multithreading flags are for a specific device being accessed across multiple threads; it's perfectly legal to have any number of devices active and in different threads or processes. They probably can't have the same focus window as they'll fight for who gets to draw.

Quote:It's just these four functions that you have to call from the main thread. All drawing and presenting can still be offloaded to a different thread, as long as this thread is the only one calling D3D functions while it lasts.
Yes, pretty much. There were some really detailed discussions about this on the DirectXDev mailing list, but they're a bit tricky to find.

The crucial detail is that once you jump the boundary into D3D you're back to being serial/single-threaded such that having multiple threads pumping commands at it doesn't actually improve performance. This is especially the case for the number of locks you'll have to take out to ensure pipeline state when rendering. It just doesn't make any sense.

Look into using NULL-REF devices, loading into scratch pools and doing DMA copies of data between threads. Typically the expensive part of loading resources from disk is the latency that magnetic/optical drives have - so simply having your worker threads stream in BLOB's that are copied over to a central D3D thread that uses something like D3DXCreateTextureFromFileInMemoryEx() tends to get a lot of the benefit without too much effort.

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

This topic is closed to new replies.

Advertisement