Jump to content
  • Advertisement
Sign in to follow this  
akaitora

Error handling in DirectX

This topic is 3619 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 guys. I am currently trying to log all directx errors in my game and was wanting to ask a general "best practice" question. How would you recommend handling errors. Currently what I am doing is like so
if(!SUCCEEDED(dev->LightEnable(0, true)))
	{
		DataLogger::getInstance()->addEntry("Could not enable the light",MESSAGE_TYPE_ERROR);
	}
However it seems that using so many succeeds for every directx device method will cause the code to get bulky and cluttered. Is this how you do logging for errors or is there a better way?

Share this post


Link to post
Share on other sites
Advertisement
I only catch errors like that for functions that "matter" if they fail - that is, functions that fill in a struct that I use (E.g. GetDeviceCaps()), or functions that create an interface (E.g. CreateVertexBuffer()). If anything else, like SetRenderState() fails, it doesn't cause the application to crash so I don't consider it worthwhile catching.
You could wrap every D3D call in a macro to assert or log if the function fails (probably only in debug builds) if you want.

In theory, nothing should fail in your main loop. SetLight() should only really fail if you pass invalid parameters to it - a bad entry in the D3DLIGHT9 struct would be a debug error, and you should be checking the device caps for the maximum number of supported lights so you can check that the light index is sane.

Share this post


Link to post
Share on other sites
I have a different approach than Steve. Sure, calling SetRenderState with bogus values isn't going to cause your application to crash, but when it returns a failed HRESULT its telling you ASAP about your problem. Calling SetRenderState with garbage isn't fatal, but its still a sign of a problem that needs fixing. Who knows what a crappy retail driver would do with bogus values from SetRenderState, particularly on a PURE device.

My approach is to use exceptions and a macro to check all HRESULTs that I don't expect to fail, but will fail when I've made a coding error. Failed HRESULTs cause an exception to be thrown with enough context that you can pinpoint the offending code, as well as a single convenient place on which to set a breakpoint when running in the debugger.

I describe the mechanism in detail in Chapter 1. Introduction of my book.

Your example would look like this in my system:

THR(dev->LightEnable(0, true));

If it failed, an exception would be thrown with the context of __FILE__ and __LINE__ of where that call to THR() appeared in your source and it would also include the text string "dev->LightEnable(0, true)" as part of the message, so you would know exactly what went wrong.

Share this post


Link to post
Share on other sites
Quote:
Original post by legalize
I describe the mechanism in detail in Chapter 1. Introduction of my book.
I've recommended your approach on these forums before and I'll vote for it again - I've used it successfully in a number of projects.

I really like the clean error handling that it introduces, much more so than if(call){}else{} type junk [smile]

An alternative is the V() and V_RETURN() macros that the SDK samples use. ISTR they also append stack trace and other info to the error message and are almost as clean and tidy as using exceptions.

hth
Jack

Share this post


Link to post
Share on other sites
I personally prefer the

hr = SomeDirectXFunction();
if (FAILED(hr)) DX_EXCEPT("error message", hr);

method of handling DirectX errors rather than wrapping the function calls directly, and I do this with pretty much every DirectX call. Mostly because I find it easier setting breakpoints, stepping through code and what not while debugging than when wrapping the calls directly in a macro. I find debugging harder than coding, so things that make debugging easier, even if it requires a little more work coding usually turn out to be a net gain for me.

Share this post


Link to post
Share on other sites
The exceptions throwing macros are essentially the mechanism that is built into SlimDX for translating failed HRESULTs into something more noticeable. That model is essentially inherited from Managed DirectX, although it's been redesigned somewhat. We also maintain the last result, so you can do GetLastError() style handling when exceptions are disabled. DXGetErrorString and DXGetErrorDescription are really handy too.

There's some advanced functionality available in SlimDX as well, like being able to ignore specific HRESULTs (lost device is a failure, but you probably don't want it to generate an exception), or request debugger breakpoints on certain codes. This is all very easy to do and I personally recommend adding it in, because it's useful functionality. You can use our Result class (h, cpp) as a guide. Result::Record is the main function. It's typically paired with a macro like:
#define RECORD_D3D10_EX(x, key, value) Result::Record<Direct3D10Exception^>( (x), (key), (value) )
#define RECORD_D3D10(x) RECORD_D3D10_EX(x, nullptr, nullptr)

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
I personally prefer the

hr = SomeDirectXFunction();
if (FAILED(hr)) DX_EXCEPT("error message", hr);

method of handling DirectX errors rather than wrapping the function calls directly, and I do this with pretty much every DirectX call. Mostly because I find it easier setting breakpoints, stepping through code and what not while debugging than when wrapping the calls directly in a macro. I find debugging harder than coding, so things that make debugging easier, even if it requires a little more work coding usually turn out to be a net gain for me.


Yes, helper functions can distract you from the real code when single-stepping in the debugger. I recommend adjusting the debugger's "step into" list through the magic .dat file to solve that problem. Then my code remains compact with appropriate things wrapped in THR() and the debugger isn't distracting me with irrelevant details when I'm single-stepping.

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!