D3D resources and Multithreading

Started by
7 comments, last by Tispe 11 years, 5 months ago
Hi

My program is using DX9 and can't share the Device among multiple threads. Which means I can't create resources on other threads.

I wanted to know if it is possible to create vertex, index buffers and textures on the main thread and then pass those resources to a worker thread that lock/read/write/unlock/release these resources while the main thread is rendering? These resources sent to the worker thread will not be in use intil the worker thread has finished and sent those resources back to the main thread.

Will this type of multithreading work for DX9? Or are resources created with the device unsafe for other threads?

Cheers!
Advertisement
I'd be careful in doing that. Even if the API allows it (D3DCREATE_MULTITHREADED) drivers might malfunction.
Yes, it's not supposed to happen. They're also not supposed to garble textures nor to crash but sometimes it happens and if you're dealing with people still on D3D9, I'd give up every thing I consider certain.
Now, I have been told this is fixed but multithreading was in its early ages and the official documentation about D3DCREATE_MULTITHREADED isn't very encouraging. D3D9 was way, way crude in its effective multi-threading capability.

Personally I would map/unmap/interact with D3D9 in the main thread and then pass the raw memory blob to the worker thread. This way D3D9 is out of the equation.

Previously "Krohm"

You can map them on the main thread, then have the worker fill them in, then unmap them on the main thread again...
How do you "map/unmap" resources from main thread to worker thread? I have never heard of this before.

In my head I create a texture or a vertex buffer on the main thread, then push a pointer to the resource to a Critical_Section vector, retrieve that from the worker thread, copy data over send it back.

Some code might help me understand ;)


EDIT: Is it as simple as Addref(), Release() when sending and retrieving it?
When you map, or lock, a resource you typically get a pointer out of it. That pointer is not thread specific and thus you can pass it off to your worker thread for it to operate on.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Sorry, by map/unmap, I meant lock/unlock. All D3D9 calls must occur on your main thread. The worker can generate data in between a call to lock and unlock.
e.g.
D3DLOCKED_RECT result = {};
texture->LockRect(0, &result, NULL, D3DLOCK_DISCARD);
SignalWorkerThreadToGenerateData( result );
... later ...
if( WorkerThreadIsDone() )
texture->UnlockRect(0);


void* result = 0;
buffer->Lock(0, 0, &result, 0);
SignalWorkerThreadToGenerateData( result );
... later ...
if( WorkerThreadIsDone() )
buffer->Unlock();
Will this work with Texture->GetSurfaceLevel (0, &Surface) and then sending the LPDIRECT3DSURFACE9 to the worker thread? I really wish to use D3DXLoadSurfaceFromMemory to convert DXTn to ARGB on the worker thread.

Will it also work with the ID3DXBaseMesh::LockVertexBuffer method if I use the ID3DXMesh wrapper?
No, you shouldn't be passing any D3D resources to the worker thread, which means not using D3DXLoadSurfaceFromMemory(). One (messy) option is to create your own class that inherits from IDirect3DSurface9, and pass an instance of that class to D3DXLoadSurfaceFromMemory(). Your class should implement all of the IDirect3DSurface9 functions.

You can then implement your class to store the ARGB pixel data internally, and then after calling D3DXLoadSurfaceFromMemory(), you can copy that ARGB data into the real surface (Which should be locked in the D3D thread and the pointer passed to the worker thread).

Or, just not use D3DXLoadSurfaceFromMemory() :)
Question: Will locking any resource block rendering, can a locked texture be locked for several frames if it is not in use?

Baw, it seems Microsoft didn't have multithreading in mind when implementng D3DXLoadSurfaceFromMemory(). I hope I can get away with calling this a few times each frame.

It seems that I will have to scrap the whole idea of passing mapped/locked resource pointers to worker threads to keep things simple. I guess I can use GetTickCount() after each resource I process and check if elapsed time is not too much, that way I can skip processing to next frame to avoid huge drops in frame rate.

This topic is closed to new replies.

Advertisement