Typical design for DirectX9's Device Lost handling

Started by
8 comments, last by imi 14 years ago
Hi, I started to write some DirectX9 code without worrying much about device-lost events. Now I want to take care of this. I am a beginner in DirectX, so please go easy on me ;-) So.. I think, the easiest way is to just put all my stuff into managed memory pool, right? Then I shouldn't be worried about device lost errors. Like http://legalizeadulthood.wordpress.com/2009/10/12/direct3d-programming-tip-9-use-the-managed-resource-pool/ recommends. Well, seems its not that simple.. I have some vertex buffers with D3DUSAGE_DYNAMIC and a couple of textures with D3DUSAGE_RENDERTARGET. It seems that both can't be used with managed memory pool. Is it wise to avoid dynamic buffers and render targets alltogether and use static vertex buffers and try to convert textures into managed ones as soon as they are rendered? Also I couldn't found a way to put my fonts into managed memory. Do I have to manually reset them too or is there some way to get them automatically reseted? If I understand it correctly, "resetting" a resource means to Release() the interface and then aquire a complete new resource, e.g. call to D3DXCreateTexture for textures again. Or is there some easier way? And Is there a typical design pattern how to handle device losts? I could imagine one global resource manager. All resources that could be lost have to be created by this manager and the places who obtain resources have to implement some kind of listener to Lost() and Reset() events. But this could get quite messy.. Or I propagate some OnDeviceLost and OnDeviceReset through my game object hierarchy (like I propagate the Paint() event every frame) and then have every individual object decide what to reset. This sounds cleaner but on second thought, the problem is, that I may not reach "currently invisible" elements (I have to make extra-sure, that I propagate the event to *all* resources, not only to the current visible ones). Sounds fragile too.. Or do you use some completely different idiom? Ciao, Imi. Edit: Before someone point this out: Yes, I read the FAQ at http://members.gamedev.net/jhoxley/directx/DirectXForumFAQ.htm about the topic, but I don't feel this questions covered there, e.g. under D3D#24, 6. it's just stated "Release all D3DPOOL_DEFAULT resources", but not how it's typically managed, hence my question here ;-) [Edited by - imi on March 19, 2010 7:02:00 PM]
Advertisement
Take a look at this

Thank you for the link, Black Knight. Unfortunately, it doesn't say anything about my question. It only talks about when a device is lost and when to reset it. The code in question about how they handle the device reset in the demo-source looks like this:

void D3DWindow::OnResetDevice(){}


:-(.

Maybe I summarize my questions again, the OP is a bit lengthy in text:

1) Do you avoid to store textures/vertex buffers with D3DUSAGE_DYNAMIC and D3DUSAGE_RENDERTARGET? (To get 100% managed resources and thus automatic lost-device handling). If so, what about fonts?

2) If not, how do you handle invalidating of resources? Do you use a global ResourceManager who spits out COM-interfaces to textures, buffers etc? If so, how do you recreate the COM-pointer without knowing the game objects they are stored in later?

(simplified)
struct ResourceManager {    IDirect3DTexture9* createTexture(const string& filename) { ... }} resMgr;...struct MyGameObject {    MyGameObject() : texture(resMgr.createTexture("foobar.png")     { texture->AddRef(); }    IDirect3DTexture9* texture;};

Here, ResourceManager doesn't know about "MyGameObject", hence he can not set it's texture member to a new value, when the device is lost.

To solve this, is there a way to re-create a texture without invalidating it's COM-pointer completely? (There has to be, as D3DPOOL_MANAGED does exactly this).

3) If there's no way, how do you solve this? Is there any typical solution for this?


Hope this was clearer? :-D

Ciao, Imi.
I don't know if this will help at all, but...

Personally, I don't bother trying to make all resources managed. I certainly put them in the managed pool when appropriate, but in some cases (such as with dynamic buffers), it's not really a viable option.

As for how to communicate to game objects that a resource has been re-created, this is a non-issue. All such resources (textures, sound effects, etc.) are managed by a wrapper object of some sort, and it is this object that game objects hold a reference to. When a resource needs to be recreated, it is the held resource *inside* the wrapper object that is recreated; the wrapper object itself is not affected, and therefore all references to it remain valid.

The framework manages various resources, including sound effects (via SDL_mixer), and various resources for both the Direct3D and OpenGL rendering backends (textures, vertex and index buffers, etc.). Some of these resources need to be recreated under various circumstances (restarting the video or audio system, reloading a modified resource during development, recovering after device loss in Direct3D), but the modules that actually use these resources don't know or care that the resource has been reloaded because the actual resource handle (e.g. OpenGL texture ID, Direct3D texture object pointer, etc.) is hidden from the client by the wrapper object.
This solves indeed the problem about "how can the resource manager set the interface pointer". But other issues are remaining.

E.g. this means your wrapper has to store not only the pointer to the texture (or other resource), but as well a complete instruction how to recreate the resource.

For textures loaded from file, this is easy: just store the filename, allthough they are not a problem as they can be put into managed pool.

I am concerned about my render target - textures. They can't be recreated easily. For example, I store the "current screen" in a texture when switching to my debug-console (or to pause mode) and overlay some information on this stored background. How do I store/recreate this information on device lost by using your wrapper-pattern?


Maybe I should switch to DirectX10, as it seems the lost device stuff is a lot easier there...

Ciao, Imi.
Quote:For example, I store the "current screen" in a texture when switching to my debug-console (or to pause mode) and overlay some information on this stored background. How do I store/recreate this information on device lost by using your wrapper-pattern?
That sounds like kind of an unusual special case, and I don't really have an answer. For most of the uses of render targets that I'm familiar with, the data is only needed for one frame or for part of one frame, so having to recreate the render target if the device is lost isn't really an issue.

If you're wanting to save a rendered frame over multiple frames, well - I'm not sure :| I suppose you could make a copy of the frame in system memory, although that could be somewhat slow, I imagine. Or maybe just copy it to system memory when you pause or switch to debug mode so that you can re-create it if needed? Or, you could just display a 'frame lost' message or something if the device is lost.

Maybe someone else can offer some better suggestions...
Quote:Original post by imi
Also I couldn't found a way to put my fonts into managed memory. Do I have to manually reset them too or is there some way to get them automatically reseted?
ID3DXFont is just a helper class. Internally it has some dynamic textures for storing the glyphs on, which is why it need OnLostDevice() and OnResetDevice() called for it.

Quote:Original post by imi
If I understand it correctly, "resetting" a resource means to Release() the interface and then aquire a complete new resource, e.g. call to D3DXCreateTexture for textures again.
Correct.

Quote:Original post by imi
To solve this, is there a way to re-create a texture without invalidating it's COM-pointer completely? (There has to be, as D3DPOOL_MANAGED does exactly this).
The managed pool has information you don't. Internally, when you create a resource in the managed pool, D3D creates a default pool and system memory pool copy of the texture; the pointer exposed to the application is the wrapper object - so that pointer doesn't change when the default pool copy is recreated.

In your case, the only option I can think of is to create a system memory copy of your render target surface after you've rendered to it, and use that to fill in the new default pool copy when you get a lost device. It's not going to be fast, because you're reading back from the GPU, but would solve your problem.
If you have any dynamic textures that are updated every frame, then you don't need to refill them when the device is reset, since they'll be filled in that frame anyway.
The only other alternative is to structure your code so the resource manager can flag a resource as needing refilled, and then your code would have to check every frame if it needs refilled, and if so, refill it (Which would be awkward in the case of your pause menu / debug console background...)
Quote:Original post by Evil Steve
Internally, when you create a resource in the managed pool, D3D creates a default pool and system memory pool copy of the texture;


Here is my understanding of default vs. managed pools. Please correct me if I'm wrong. For most textures in a typical game, that sounds like a huge waste of system memory. For most textures for example, a game already has the information of where to load them from readily available, and recreating them is usually (or should be) a matter of calling some CreateD3DResources() once again. Given the relatively high system memory requirements of games these days, I'm guessing most use managed resources for all but dynamic/RenderTarget stuff.

However, using managed resources is not just a way to tell D3D that you don't feel like reloading stuff yourself. The 2 main advantages are: 1) Reloading textures from memory is (much?) faster than loading them from files, making e.g. display mode changes faster. And 2) Using managed resources relieves the application from the task of keeping track of video memory usage and, well, resource management, ensuring there is enough space for needed resources and evicting "unneeded" ones if necessary. D3D does that mainly by evicting the least-recently-used resources, but possibly takes into consideration other factors as well, like the amount of video memory a resource actually uses.
Quote:Original post by Amr0
Here is my understanding of default vs. managed pools. Please correct me if I'm wrong. For most textures in a typical game, that sounds like a huge waste of system memory. For most textures for example, a game already has the information of where to load them from readily available, and recreating them is usually (or should be) a matter of calling some CreateD3DResources() once again. Given the relatively high system memory requirements of games these days, I'm guessing most use managed resources for all but dynamic/RenderTarget stuff.

However, using managed resources is not just a way to tell D3D that you don't feel like reloading stuff yourself. The 2 main advantages are: 1) Reloading textures from memory is (much?) faster than loading them from files, making e.g. display mode changes faster. And 2) Using managed resources relieves the application from the task of keeping track of video memory usage and, well, resource management, ensuring there is enough space for needed resources and evicting "unneeded" ones if necessary. D3D does that mainly by evicting the least-recently-used resources, but possibly takes into consideration other factors as well, like the amount of video memory a resource actually uses.
Yes - the managed pool is just a helper really. Unfortunately, it can be difficult to implement your own resource manager and use D3DPOOL_MANAGED at the same time, since both will fight for VRAM - so you'd be best to implement everything in your own managed system - which could be a lot of work.
I got it to work now. My render-targets weren't that hard to change in a way that they can be recreated as necessary. I just setup a render target and call to the paint code manually. Looks much more robust now anyway..

For the other resources, I introduced wrappers and use boost::signal library to register on a lostDevice and resetDevice event. (This actually works nicely with things like the Font's OnLostDevice member function, so I even don't need the wrapper for fonts.)

As for MANAGED-textures: They are gone when the graphic settings change, but at the moment I hesitate to write my own manager around them. Instead I abort the program with some "please restart" when I detect it. Not the nice way, but actually a lot other games do it too. :-P

Thanks to all for suggestions and explanations.

Ciao, Imi.

This topic is closed to new replies.

Advertisement