Having a recording of what was on the screen when a game raises an exception is a useful tool for debugging. The recording shows what the game was displaying when the error occurred. This can help you both in pin pointing the state of the program and in repeating the error conditions when testing fixes.
Before you can use Game DVR as an exception-handling aid during development, you’ll need to ensure that you have set up a user account on Xbox Live that is provisioned to access the sandbox configured for your title. This is the account that you’ll use to conduct run-time testing of your title. Account provisioning to allow access to the sandbox is accomplished through the Xbox Developer Portal (XDP); contact your developer account manager for more information.
Next, you’ll have to verify that your project’s manifest file contains the required Xbox Live extensions:
<Extension Category="xbox.live">
<XboxLive TitleId="YourTitleId" PrimaryServiceConfigId="YourSandboxId" />
</Extension>
The final step in getting started is to set the SandboxID of your development console using the ID that is configured in XDP.
At clip-creation time, you’ll need to provide some specific information to the ApplicationClipInfo object. Two class members of interest are the GreatestMomentId (required) and TitleData (optional) parameters. Specifying the exception type as part of the GreatestMomentId parameter can also be useful for future reference.
TitleData is a string that gets stored with the rest of the video’s metadata. You can use the TitleData metadata field to include notes or information related to the exception (such as variable values, game state, thread IDs, and so on). The following example encapsulates these steps into a function that will be referenced later in this paper:
void CaptureVideoOfError( Platform::String^ exceptionType,
Platform::String^ titleData )
{
ApplicationClipInfo^ clipInfo = ref new ApplicationClipInfo( exceptionType );
clipInfo->TitleData = ref new Platform::String( titleData );
...
When you call the ApplicationClipCapture::RecordTimespanAsync() method, the ApplicationClipInfo object created in the example should be used. Next, specify the current system time for the end-time parameter. For the start-time parameter, specify a point in time within the past five minutes (the length of the recording buffer).
// Set up the clip to retrieve the past 30 seconds
DateTime start, end;
::GetSystemTimeAsFileTime( reinterpret_cast< FILETIME* >( &end ) );
start.UniversalTime = end.UniversalTime - 30L * 10000000L;
ApplicationClipCapture appCapture = ref new ApplicationClipCapture();
auto asyncOp = appCapture->RecordTimespanAsync( user, clipInfo, start, end );
...
} // CaptureVideoOfError()
For exceptions that are handled in try/catch blocks, you can start the Game DVR calls in your catch block. You don’t need to do this in a separate thread, because the Game DVR processes will create another thread internally.
try
{
// Game logic
}
catch ( Platform::Exception^ ex )
{
CaptureVideoOfError( ex->GetType()->ToString()->Begin(),
L"YourDebugMessageHere" );
}
The same technique can be applied to other error-detection methods, including HRESULT value checks. For exceptions or asserts that can be continued, completing the current frame and then blocking until the GPU is idle might also be helpful. In some cases, a single screenshot of the frame where the exception occurred might be just as useful as the last several seconds of gameplay video.
Using Game DVR to record clips when unhandled exceptions occur follows a pattern that is very similar to the technique used for handled exceptions.
First, you’ll need to enable your title to supersede the top-level exception handler. You can do this by using the SetUnhandledExceptionFilter() function, specifying a custom callback to handle the exception:
int main()
{
SetUnhandledExceptionFilter( YourExceptionHandler );
...
}
Within YourExceptionFilter(), perform the same steps as were outlined in the preceding section.
LONG WINAPI YourExceptionHandler( PEXCEPTION_POINTERS exceptionInfo )
{
HANDLE hThread = CreateThread( NULL, 0, CaptureDVRClip,
( LPVOID ) exceptionInfo, 0, NULL );
WaitForSingleObject( hThread, 10000 );
...
}
DWORD WINAPI CaptureDVRClip( LPVOID param )
{
...
PEXCEPTION_POINTERS& exceptionInfo =
*reinterpret_cast< PEXCEPTION_POINTERS* >( lpParam );
CaptureVideoOfError( ref new Platform::String( L"UnhandledException" ),
pException->ExceptionRecord->ExceptionCode.ToString() );
}
After the MP4 clip is created locally, the clip will automatically be uploaded to the user’s storage in the cloud. These clips can be queried later on by enumeration (refer to the Xbox One documentation for more details on querying clips), and the local copy of the clip will be marked for deletion. Holding onto the instance of the ApplicationClip will prevent the file from being deleted.
Clips can also be queried by GreatestMomentId. Specifying the exception type for this parameter during clip creation, as recommended earlier, enables you to query all clips related to that exception.
Although the majority of Game DVR processes are executed in system-reserved memory, we recommend that your title have at least 5MB of available memory at the time the exception occurs. This number will vary based on how you decide to record the clip (such as in a separate thread, with helper variables, and so on).
GlobalMemoryStatusEX() function is not guaranteed to be accurate, and therefore not recommended to retrieve available memory. Instead, you may rely on PIX counters to measure memory usage.
The memory requirements specified in the preceding section implies that you will almost certainly need to free up some memory in case your title encounters an OutOfMemoryException event (underlying HRESULT = E_OUTOFMEMORY). This will also be true in the case of stack-corruption errors, which are less predictable.
Finally, it’s important to note that Game DVR functionality for debug purposes should always be compiled out before you submit your title for certification. This will avoid unintentional game-screen recording and sharing.