Sign in to follow this  
blueshogun96

GPU load/temp monitoring for Intel?

Recommended Posts

blueshogun96    2265

Okay, I've been given the task to write a GPU load and temperature monitoring tool as part of an automated test suite for my job.  Since I'm under NDA, I won't get too detailed about my project outside of what is publicly available.  We have two devices, one with an Intel 4600 GPU, and the other is a Quadro K2200.  For the NV stuff, that's easy.  I can use NVAPI to get exactly what I need.  For Intel, I can't seem to find a similar library.  If necessary, I may have to do something on the driver level, hopefully not, but this has got to get done ASAP and we're shipping in Q1 next year.

 

As usual, I tried doing a bit of googling, and wasn't able to find what I needed.  Any ideas?  Thanks.

 

Shogun.

Share this post


Link to post
Share on other sites
blueshogun96    2265

Thanks for the responses, but even if that's the solution for the temperature monitor, that's only half of my problem.  The GPU load (usage) value is likely more important to my boss right now.  I came up with the temperature thing on my own.  I thought DirectX had an API for this?

 

Shogun.

Share this post


Link to post
Share on other sites
frob    44969

The folks who made GPU-Z have a third-party library that works across cards.
 
Some of the information can be found with D3DKMTQueryStatistics, which is usually not meant for user-mode code. For a source-code example, ProcessHacker has source as well as header files for the structures you're going to need.  
 
Beware that these calls are venturing off into undocumented, unsupported, and vendor-specific territory.

Share this post


Link to post
Share on other sites
blueshogun96    2265

The folks who made GPU-Z have a third-party library that works across cards.
 
Some of the information can be found with D3DKMTQueryStatistics, which is usually not meant for user-mode code. For a source-code example, ProcessHacker has source as well as header files for the structures you're going to need.  
 
Beware that these calls are venturing off into undocumented, unsupported, and vendor-specific territory.

Ah, so that's what they did.  I knew that if GPU-Z could do it, then it had to be possible.

 

ProcessHacker seems to have lots of interesting code that could likely get me where I need to be.  Since this is essentially going to be a private inhouse tool for an embedded Windows 10 based hardware platform by Microsoft (that should kinda give you a hint as to what hardware I'm working with), going beyond user mode is not going to be an issue.  I've dealt with some driver level and a hint of kernel level stuff before (primarly on Xbox), so I think I'll have a good idea of what risks not to take :)

 

 

Try this: https://msdn.microsoft.com/en-us/library/windows/desktop/aa371886%28v=vs.85%29.aspx

There are many counters under Processor, perhaps on integrated GPUs there's a counter for it specifically.

I'll also take a look at this too.  It may or may not be what I need, but I think this is also worth noting/learning.

 

Shogun.

Share this post


Link to post
Share on other sites
blueshogun96    2265

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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this