Okay, I wrote some basic code for the NV driver. Turns out that NVAPI doesn't directly expose the API needed to get your GPU load percentage. So I had to implement a workaround based on this guy's blog example: http://eliang.blogspot.com/2011/05/getting-nvidia-gpu-usage-in-c.html
It's not the safest way, but it does work for both 32 and 64 bit, and 64-bit is our target.
The code I wrote based off of this (sharing for future programmers who may need this years down the line).
#ifndef NV_H
#define NV_H
#include <Windows.h>
#include <iostream>
//#include <nvapi.h>
/*
* Name: NvGpuDetected
* Desc: Returns true if an NVIDIA Gpu has been detected on this system.
* NOTE: This function depends on whether a valid NVIDIA driver is installed
* on the target machine. Since the Surface Hub does not include an
* nvapi[64].dll in it's strippified driver, we need to load it directly
* in order for the required APIs to work.
*/
BOOL NvGpuDetected();
/*
* Name: NvGetGpuLoad
* Desc: Returns the NVIDIA Gpu's current load percentage.
*/
int NvGetGpuLoad();
/*
* Name: NvGetGpuTemperature
* Desc: Returns the current temperature of an NVIDIA Gpu.
*/
int NvGetGpuTemperature();
#endif // NV_H
#include "nvidia.h"
#if defined(_M_X64) || defined(__amd64__)
#define NVAPI_DLL "nvapi64.dll"
#else
#define NVAPI_DLL "nvapi.dll"
#endif
// magic numbers, do not change them
#define NVAPI_MAX_PHYSICAL_GPUS 64
#define NVAPI_MAX_USAGES_PER_GPU 34
// function pointer types
typedef int *(*NvAPI_QueryInterface_t)(unsigned int offset);
typedef int (*NvAPI_Initialize_t)();
typedef int (*NvAPI_EnumPhysicalGPUs_t)(int **handles, int *count);
typedef int (*NvAPI_GPU_GetUsages_t)(int *handle, unsigned int *usages);
// nvapi.dll internal function pointers
NvAPI_QueryInterface_t NvAPI_QueryInterface = NULL;
NvAPI_Initialize_t NvAPI_Initialize = NULL;
NvAPI_EnumPhysicalGPUs_t NvAPI_EnumPhysicalGPUs = NULL;
NvAPI_GPU_GetUsages_t NvAPI_GPU_GetUsages = NULL;
/*
* Name: NvGpuDetected
* Desc: Returns true if an NVIDIA Gpu has been detected on this system.
* NOTE: This function depends on whether a valid NVIDIA driver is installed
* on the target machine. Since the Surface Hub does not include an
* nvapi[64].dll in it's strippified driver, we need to load it directly
* in order for the required APIs to work.
*/
BOOL NvGpuDetected()
{
HMODULE hmod = LoadLibraryA( NVAPI_DLL );
if( hmod == NULL )
{
std::cerr << "Couldn't find " << NVAPI_DLL << std::endl;
return FALSE;
}
// nvapi_QueryInterface is a function used to retrieve other internal functions in nvapi.dll
NvAPI_QueryInterface = (NvAPI_QueryInterface_t) GetProcAddress( hmod, "nvapi_QueryInterface" );
// some useful internal functions that aren't exported by nvapi.dll
NvAPI_Initialize = (NvAPI_Initialize_t) (*NvAPI_QueryInterface)(0x0150E828);
NvAPI_EnumPhysicalGPUs = (NvAPI_EnumPhysicalGPUs_t) (*NvAPI_QueryInterface)(0xE5AC921F);
NvAPI_GPU_GetUsages = (NvAPI_GPU_GetUsages_t) (*NvAPI_QueryInterface)(0x189A1FDF);
if( NvAPI_Initialize == NULL || NvAPI_EnumPhysicalGPUs == NULL ||
NvAPI_EnumPhysicalGPUs == NULL || NvAPI_GPU_GetUsages == NULL )
{
std::cerr << "Couldn't get functions in nvapi.dll" << std::endl;
return FALSE;
}
// initialize NvAPI library, call it once before calling any other NvAPI functions
if( (*NvAPI_Initialize)() != 0 )
{
std::cerr << "Could not initialize nvapi!" << std::endl;
}
return TRUE;
}
/*
* Name: NvGetGpuLoad
* Desc: Returns the NVIDIA Gpu's current load percentage.
*/
int NvGetGpuLoad()
{
int gpuCount = 0;
int *gpuHandles[NVAPI_MAX_PHYSICAL_GPUS] = { NULL };
unsigned int gpuUsages[NVAPI_MAX_USAGES_PER_GPU] = { 0 };
// gpuUsages[0] must be this value, otherwise NvAPI_GPU_GetUsages won't work
gpuUsages[0] = (NVAPI_MAX_USAGES_PER_GPU * 4) | 0x10000;
(*NvAPI_EnumPhysicalGPUs)( gpuHandles, &gpuCount );
(*NvAPI_GPU_GetUsages)( gpuHandles[0], gpuUsages );
int usage = gpuUsages[3];
return 0;
}
/*
* Name: NvGetGpuTemperature
* Desc: Returns the current temperature of an NVIDIA Gpu.
*/
int NvGetGpuTemperature()
{
return 0; // TODO
}
#if 0
int main()
{
#if defined(_M_X64) || defined(__amd64__)
HMODULE hmod = LoadLibraryA("nvapi64.dll");
#else
HMODULE hmod = LoadLibraryA("nvapi.dll");
#endif
if (hmod == NULL)
{
std::cerr << "Couldn't find nvapi.dll" << std::endl;
return 1;
}
// nvapi.dll internal function pointers
NvAPI_QueryInterface_t NvAPI_QueryInterface = NULL;
NvAPI_Initialize_t NvAPI_Initialize = NULL;
NvAPI_EnumPhysicalGPUs_t NvAPI_EnumPhysicalGPUs = NULL;
NvAPI_GPU_GetUsages_t NvAPI_GPU_GetUsages = NULL;
// nvapi_QueryInterface is a function used to retrieve other internal functions in nvapi.dll
NvAPI_QueryInterface = (NvAPI_QueryInterface_t) GetProcAddress(hmod, "nvapi_QueryInterface");
// some useful internal functions that aren't exported by nvapi.dll
NvAPI_Initialize = (NvAPI_Initialize_t) (*NvAPI_QueryInterface)(0x0150E828);
NvAPI_EnumPhysicalGPUs = (NvAPI_EnumPhysicalGPUs_t) (*NvAPI_QueryInterface)(0xE5AC921F);
NvAPI_GPU_GetUsages = (NvAPI_GPU_GetUsages_t) (*NvAPI_QueryInterface)(0x189A1FDF);
if (NvAPI_Initialize == NULL || NvAPI_EnumPhysicalGPUs == NULL ||
NvAPI_EnumPhysicalGPUs == NULL || NvAPI_GPU_GetUsages == NULL)
{
std::cerr << "Couldn't get functions in nvapi.dll" << std::endl;
return 2;
}
// initialize NvAPI library, call it once before calling any other NvAPI functions
(*NvAPI_Initialize)();
int gpuCount = 0;
int *gpuHandles[NVAPI_MAX_PHYSICAL_GPUS] = { NULL };
unsigned int gpuUsages[NVAPI_MAX_USAGES_PER_GPU] = { 0 };
// gpuUsages[0] must be this value, otherwise NvAPI_GPU_GetUsages won't work
gpuUsages[0] = (NVAPI_MAX_USAGES_PER_GPU * 4) | 0x10000;
(*NvAPI_EnumPhysicalGPUs)(gpuHandles, &gpuCount);
// print GPU usage every second
for (int i = 0; i < 100; i++)
{
(*NvAPI_GPU_GetUsages)(gpuHandles[0], gpuUsages);
int usage = gpuUsages[3];
std::cout << "GPU Usage: " << usage << std::endl;
Sleep(1000);
}
return 0;
}
#endif
Working on a D3DKMT version for Intel and everything else. I'll share that if/when it works.
Shogun.