Using NVAPI for SLI Detection

The advice in the previous sections involves some changes to your application that may only be required when running in SLI configurations. In addition, some of the changes may require knowing how many AFR Groups exist in the currently running configuration. This section explains how you can use NVAPI in your application to detect whether it's running in an SLI AFR configuration and how many AFR Groups exist in that configuration.

NVAPI is a lightweight library included as part of the end user's driver installation, which allows applications to query a number of important details regarding the user's system configuration. Generally speaking, there are at least two tasks you would use NVAPI for when developing for SLI: (1) querying the number of SLI-configured GPUs on the system, and (2) querying the current AFR index you're rendering to per-frame.

To use NVAPI, first you need to initialize it:

#pragma comment (lib, "nvapi.lib")
// File containing nvapi interface
#include "nvapi.h"

NvAPI_Status status;
status = NvAPI_Initialize();
if (status != NVAPI_OK)
{
NvAPI_ShortString string;
NvAPI_GetErrorMessage(status, string);
printf("NVAPI Error: %s\n", string);
return false;
}

It's a good practice to check the driver ID string to ensure NVAPI is working properly with the system configuration:

NV_DISPLAY_DRIVER_VERSION version = {0};
version.version = NV_DISPLAY_DRIVER_VERSION_VER;

status = NvAPI_GetDisplayDriverVersion(DEFAULTER, &version);

if (status != NVAPI_OK)
{
NvAPI_ShortString string;
NvAPI_GetErrorMessage(status, string);
printf("NVAPI Error: %s\n", string);
return false;
}

Once the NVAPI library is initialized and the driver is verified, you can use NvAPI_D3D_GetCurrentSLIState to query the current SLI state.

The following is the reference documentation for this call from nvapi.h:

typedef struct
{
NvU32 version; // Structure version (must be set to NV_GET_CURRENT_SLI_STATE_VER)
NvU32 maxNumAFRGroups; // [OUT] The maximum possible value of numAFRGroups
NvU32 numAFRGroups; // [OUT] The number of AFR groups enabled in the system
NvU32 currentAFRIndex; // [OUT] The AFR group index for the frame currently being rendered
NvU32 nextFrameAFRIndex; // [OUT] What the AFR group index will be for the next frame // (i.e. after calling Present)
NvU32 previousFrameAFRIndex; // [OUT] The AFR group index that was used for the previous frame
// (~0 if this is the first frame)
NvU32 bIsCurAFRGroupNew; // [OUT] boolean: Is this frame the first time running on the current
// AFR group
} NV_GET_CURRENT_SLI_STATE;
#define NV_GET_CURRENT_SLI_STATE_VER MAKE_NVAPI_VERSION(NV_GET_CURRENT_SLI_STATE,1)

Remember that an AFR Group can consist of either one GPU working on a given frame or multiple GPUs working on the same frame in SFR mode. In addition, note there are two different variables in the NV_GET_CURRENT_SLI_STATE struct that provide a number of AFR Groups: maxNumAFRGroups and numAFRGroups. This is because it is possible for the number of active AFR Groups to change dynamically while the application is running in some circumstances, such as when an SLI-enabled laptop is unplugged, switching to running on batteries. For this reason, we recommended that the application calls NvAPI_D3D_GetCurrentSLIState once per frame to detect potential changes to the number of active AFR Groups. The recommended usage pattern, per the example code below, is to check the value of maxNumAFRGroups once at application start time, and then check numAFRGroups and bIsCurAFRGroupNew at the beginning of every frame.

Int gNumSLIGPUs = 0;
bool bSLIEnabled = false;

NV_GET_CURRENT_SLI_STATE sliState;
sliState.version = NV_GET_CURRENT_SLI_STATE_VER;
status = NvAPI_D3D_GetCurrentSLIState( pd3dDevice, &sliState);
if ( status != NVAPI_OK ) {
/*
* Error code here
* Requesting a NVIDIA driver update
*is the preferred action in this case
*/
}
else {
gMaxNumAFRGroups = sliState.maxNumAFRGroups;
gNumAFRGroups = sliState.numAFRGroups;
bSLIEnabled = (gMaxNumAFRGroups > 1);
}
/*
* While in Render Loop
* One cycle iteration stands for one frame
*/
while(1)
{
status = NvAPI_D3D_GetCurrentSLIState(pDevice, &sliState);
if (status !== NVAPI_OK)
{
if (sliState.bIsCurAFRGroupNew)
{
/*
* Rendering on a GPU AFR group which was recently
* added to a list of active AFR groups or has never drawn
* a frame yet.
* Do any initialization work here.
* Example: sometimes it’s usefully to hold timers for
* the individual AFR groups so this is were you would
* initialize the timers.
*/
GPUtimers[sliState.currentAFRIndex] =
GPUtimers[sliState.previousFrameAFRIndex];
/* Update our copy of numAFRGroups.
* This may affect some of the logic, for example
* to handle Asynchronous D3D queries
*/
gNumAFRGroups = sliState.numAFRGroups;
}
/*
* Do render updates based on AFR Index
* Render Render Targets that are specific to only
* this GPU.
*/
Update_GPU(sliState.currentAFRIndex);
GPUtimers[sliState.currentAFRIndex] = globalTimer;
}
}

 

 


NVIDIA® GameWorks™ Documentation Rev. 1.0.200608 ©2014-2020. NVIDIA Corporation. All Rights Reserved.