So, an issue that I have been battling for about a week involved resetting devices in Direct X. I wanted to cover how to do this and the possible downfalls you might run into while performing something that should be fairly straight forward to accomplish.
Q. When would you need to reset the device?
A. Anytime the device becomes "lost", this could occur when you resize the viewport, minimize the window, switch between fullscreen and windowed modes, and when in fullscreen, giving focus to another window other than the application which is running direct x.
Q. Why is this so difficult?
A. That is a great question, I am not sure. It seems like the Direct X interface should be smart enough to recover itself in the event that the device is lost. This is not the case. Not only is it not able to recover itself, it requires quite a bit of tender loving care when resetting occurs.
Q. What is required to reset a device.
A. After you have set up the code to create a device, your application loop must regularly check to see if the device is still active. This can be performed by calling the Device.TestCooperativeLevel() function. This function tests the device to determine if it is in a state ready to render. The possible outcomes of this function call are D3D_OK, in which case you may proceed to render your scene, D3DERR_DRIVERINTERNALERROR, in which case the device is in a bad state or the drivers are unable to process commands for some reason. The other two states are the important ones, these are D3DERR_DEVICELOST and D3DERR_DEVICENOTRESET. These indicate that the device is lost and needs to be reset.
Once either of the Device Lost or Device Not Reset errors are identified, code must be written to reset the device. The important thing to understand is that the device is very stupid about how it handles passing out resources. As such, you must ensure that the device reset occurs on the same thread as it was created. Additionally, Microsoft documentation indicates that the device releases all explicit render targets, depth stencil surfaces, additional swap chains, state blocks and default resources associated with the device. This means that any resource that is returned from the Device "Get" functions that return pointers to resources, must have their resource.release() function called on them to notify the device that the resource is no longer required. The down side to this is that any of these resources that will be required again once the device is reacquired, must be recreated before they can be used again.
The most frustrating thing here is that the Microsoft documentation states that these resources SHOULD be released, however, it is my experience that failure to release these resources causes the reset to fail, returning an D3DERR_INVALIDCALL. As a result, it really isn't just recommended, but a must, to ensure that all modules that request resources from the device have both a OnLostDevice and OnResetDevice functions that handle freeing and recreating these resources.
Another note, it is also a good idea to call the Device.EvictManagedResources function. This function removes any additional resources that were allocated using the managed resource pool. See this article for more information on the managed resource pool. Using the managed resource pool can also help prevent the need for the OnLostDevice and OnResetDevice functions for your modules, however there is a cost/benefit trade off here that must be understood and should be researched before choosing either method of resource management.
Once all resources have been released, we can reset the device. To do so, we must first build a set of Present Parameters similar to what we created when first creating the Direct X Device. This is the tricky part as there are many options here to set and any bad combination of these will cause the device to fail to reset. The important thing to note here is that you need some way for your application to know if it is in windowed mode or fullscreen mode when it resets (assuming that you want your application to have both a fullscreen and windowed modes). The reason for this is that depending on the mode, different settings require different values. For example, in fullscreen mode, you need to specify BackBufferWidth and BackBufferHeight values that equal a value that is equivalent to a supported display resolution. These can be retrieved using the EnumAdapterModes function. If the application is a windowed application, these values need to be set to the dimensions of the HWND object that is stored in the hDeviceWindow of the Present Parameters. Also, importantly, FullScreen_RefreshRateInHz should be set to the window resolution selected from the EnumAdaptersModes function in fullscreen mode, but should always be set to 0 in windowed mode, otherwise the reset will fail.
The rest of the parameters can be referenced here. Rules for setting each of them are listed. Be sure to verify that each of them are set accordingly depending on what mode your device is in.
Finally, reset the device by calling the device.reset(presentParameters) function. This will attempt to reset the device. If it was successful, it will return a D3D_OK and you should proceed with recreating all of your required resources by calling the OnResetDevice functions of your applications modules. If it does not, this is where the difficulty comes in. In most failure events the function will return D3DERR_INVALIDCALL, if this occurs then check your parameters to make sure they are correct. Otherwise, it is most likely due to a non-released resource. Running your application using the debug version of the Direct X dll will improve the quality of the error message received from the Direct X driver. This may not be an option for you if your application is already pushing the limits of available frame rates.
I hope that this tutorial has been helpful. I know that when I went looking for information on how this stuff works, the tutorials were brief, mostly directed at solving specific problems and less about the actual theory behind the information. I cannot claim to be a Direct X expert and if any of the information in this article is false, as always feel free to correct me and I will update the tutorial.
No comments:
Post a Comment