Last active
April 26, 2021 07:03
-
-
Save StefanoBelli/3fe285f4aa28b460241b85a0461faa14 to your computer and use it in GitHub Desktop.
nvapi interface
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstring> | |
#include <Windows.h> | |
#include <string> | |
#include <iostream> | |
#include <memory> | |
#include <vector> | |
#define MAKE_VERSION(stype, ver) static_cast<std::uint32_t>( sizeof(stype) | ((ver) << 16) ) | |
//generic type for Nvapi handles | |
using NvHandle = int*; | |
enum class NvGpuPublicClockId { | |
GRAPHICS, | |
MEMORY, | |
PROCESSOR, | |
VIDEO, | |
UNDEFINED | |
}; | |
enum class NvGpuPerfPstate20ClockTypeId { | |
SINGLE, | |
RANGE | |
}; | |
enum class NvGpuPerfVoltageInfoDomainId { | |
CORE, | |
UNDEFINED | |
}; | |
struct NvGpuPerfPstates20ParamDelta { | |
std::uint32_t value; | |
struct { | |
std::uint32_t max; | |
std::uint32_t min; | |
} range; | |
}; | |
struct NvGpuPstate20ClockEntry { | |
NvGpuPublicClockId domainId; | |
NvGpuPerfPstate20ClockTypeId typeId; | |
std::uint32_t bIsEditable : 1; | |
std::uint32_t reserved : 31; | |
NvGpuPerfPstates20ParamDelta freqDeltaKHz; | |
union { | |
struct { | |
std::uint32_t freqKHz; | |
} single; | |
struct { | |
std::uint32_t minFreqKHz; | |
std::uint32_t maxFreqKHz; | |
NvGpuPerfVoltageInfoDomainId domainId; | |
std::uint32_t minVoltageuV; | |
std::uint32_t maxVoltageuV; | |
} range; | |
} data; | |
}; | |
struct NvGpuPstate20BaseVoltageEntry { | |
NvGpuPerfVoltageInfoDomainId domainId; | |
std::uint32_t bIsEditable : 1; | |
std::uint32_t reserved : 31; | |
std::uint32_t voltuV; | |
NvGpuPerfPstates20ParamDelta paramDelta; | |
}; | |
struct NvGpuPerfPstates20Info { | |
std::uint32_t version; | |
std::uint32_t bIsEditable : 1; | |
std::uint32_t reserved : 31; | |
std::uint32_t numPstates; | |
std::uint32_t numClocks; | |
std::uint32_t numBaseVoltages; | |
struct { | |
int pstateId; //0 - 16 pstate level | |
std::uint32_t bIsEditable : 1; | |
std::uint32_t reserved : 31; | |
NvGpuPstate20ClockEntry clocks[8]; | |
NvGpuPstate20BaseVoltageEntry baseVoltages[4]; | |
} pstates[16]; | |
}; | |
constexpr std::uint32_t PERF_PSTATES20_INFO_V1 = MAKE_VERSION(NvGpuPerfPstates20Info, 1); | |
class NvApiInterface { | |
public: | |
NvApiInterface(NvApiInterface const&) = delete; | |
NvApiInterface(NvApiInterface&&) = delete; | |
NvApiInterface& operator=(NvApiInterface&&) = delete; | |
NvApiInterface& operator=(NvApiInterface const&) = delete; | |
NvApiInterface(HMODULE rtHal) { | |
std::memset(rawGenericData, 0, sizeof(rawGenericData)); | |
getVersionStringCallback = | |
callback_cast(GetProcAddress(rtHal, | |
"?GetVersionString@CNVAPIInterface@@QAE?AW4_NvAPI_Status@@PAD@Z")); | |
getPhysicalGPUHandles = | |
callback_cast(GetProcAddress(rtHal, | |
"?GetPhysicalGPUHandles@CNVAPIInterface@@QAE?AW4_NvAPI_Status@@PAPAUNvPhysicalGpuHandle__@@PAK@Z")); | |
gpuGetFullName = | |
callback_cast(GetProcAddress(rtHal, | |
"?GpuGetFullName@CNVAPIInterface@@QAE?AW4_NvAPI_Status@@PAUNvPhysicalGpuHandle__@@PAD@Z")); | |
gpuGetPstates20 = | |
callback_cast(GetProcAddress(rtHal, | |
"?GpuGetPstates20@CNVAPIInterface@@QAE?AW4_NvAPI_Status@@PAUNvPhysicalGpuHandle__@@PAUNV_GPU_PERF_PSTATES20_INFO_V1@@@Z")); | |
gpuSetPstates20 = | |
callback_cast(GetProcAddress(rtHal, | |
"?GpuSetPstates20@CNVAPIInterface@@QAE?AW4_NvAPI_Status@@PAUNvPhysicalGpuHandle__@@PAUNV_GPU_PERF_PSTATES20_INFO_V1@@@Z")); | |
Callback ctor = callback_cast(GetProcAddress(rtHal, "??0CNVAPIInterface@@QAE@XZ")); | |
Callback uninit = callback_cast(GetProcAddress(rtHal, "?Uninit@CNVAPIInterface@@QAEXXZ")); | |
Callback init = callback_cast(GetProcAddress(rtHal, "?Init@CNVAPIInterface@@QAE?AW4_NvAPI_Status@@XZ")); | |
__asm { | |
mov ecx, this | |
mov eax, ctor | |
call eax | |
mov eax, uninit | |
call eax | |
mov eax, init | |
call eax | |
} | |
__dtor = callback_cast(GetProcAddress(rtHal, "??1CNVAPIInterface@@UAE@XZ")); | |
} | |
~NvApiInterface() { | |
__asm { | |
mov ecx, this | |
mov eax, __dtor | |
add eax, this | |
call[eax] | |
} | |
} | |
std::string GetVersionString() const { | |
char buf[64]{ 0 }; | |
__asm { | |
lea eax, buf | |
push eax | |
mov ecx, this | |
mov eax, getVersionStringCallback | |
add eax, this | |
call[eax] | |
} | |
return buf; | |
} | |
std::vector<NvHandle> GetPhysicalGPUHandles() const { | |
NvHandle addr[64]; | |
unsigned len; | |
__asm { | |
lea eax, len | |
push eax | |
lea eax, addr | |
push eax | |
mov ecx, this | |
mov eax, getPhysicalGPUHandles | |
add eax, this | |
call[eax] | |
} | |
//we could also cache these values... | |
std::vector<NvHandle> handles; | |
for (unsigned hndIdx = 0; hndIdx < len; ++hndIdx) | |
handles.push_back(addr[hndIdx]); | |
return handles; | |
} | |
std::string GpuGetFullName(NvHandle gpuHandle) const { | |
char gpuFullName[64]{ 0 }; | |
__asm { | |
lea eax, gpuFullName | |
push eax | |
mov eax, gpuHandle | |
push eax | |
mov ecx, this | |
mov eax, gpuGetFullName | |
add eax, this | |
call[eax] | |
} | |
return gpuFullName; | |
} | |
NvGpuPerfPstates20Info GpuGetPstates20(NvHandle gpuHandle) const { | |
NvGpuPerfPstates20Info info; | |
info.version = PERF_PSTATES20_INFO_V1; | |
__asm { | |
lea eax, info | |
push eax | |
mov eax, gpuHandle | |
push eax | |
mov ecx, this | |
mov eax, gpuGetPstates20 | |
add eax, this | |
call[eax] | |
} | |
return info; | |
} | |
int GpuSetPstate20(NvHandle gpuHandle, NvGpuPerfPstates20Info info) { | |
__asm { | |
lea eax, info | |
push eax | |
mov eax, gpuHandle | |
push eax | |
mov ecx, this | |
mov eax, gpuSetPstates20 | |
add eax, this | |
call[eax] | |
} | |
} | |
private: | |
typedef void(*Callback)(); | |
constexpr Callback callback_cast(void* fptr) { | |
return reinterpret_cast<Callback>(fptr); | |
} | |
// data begin | |
int rawGenericData[100]; // 400 Bytes | |
// data end | |
Callback getVersionStringCallback; | |
Callback getPhysicalGPUHandles; | |
Callback gpuGetFullName; | |
Callback gpuGetPstates20; | |
Callback gpuSetPstates20; | |
Callback __dtor; | |
}; | |
// | |
//example usage of the interface! | |
// | |
struct HmoduleDelete { | |
public: | |
void operator()(void* ptr) const { | |
FreeLibrary(static_cast<HMODULE>(ptr)); | |
} | |
}; | |
using ScopedHmodule = std::unique_ptr<void, HmoduleDelete>; | |
int main() { | |
SetDllDirectoryA("C:\\Windows\\Syswow64"); | |
SetDllDirectoryA("D:\\ssynx\\Devel\\rthal_bin"); //replace with Afterburner or other piece of shit OC tool | |
ScopedHmodule nvapi(LoadLibraryA("nvapi.dll")); | |
if (nvapi == nullptr) { | |
std::cerr << "could not load nvapi.dll\n"; | |
return 1; | |
} | |
ScopedHmodule hal(LoadLibraryA("rthal.dll")); | |
if (hal == nullptr) { | |
std::cerr << "could not load rthal.dll\n"; | |
return 1; | |
} | |
NvApiInterface nvapiIntf(static_cast<HMODULE>(hal.get())); | |
std::vector<NvHandle> physicalGpus = nvapiIntf.GetPhysicalGPUHandles(); | |
NvGpuPerfPstates20Info pstates = | |
nvapiIntf.GpuGetPstates20(physicalGpus[0]); | |
std::cout << "NvAPI version: " << nvapiIntf.GetVersionString() << '\n' | |
<< "Physical GPUs: " << physicalGpus.size() << '\n' | |
<< "First GPU name: " << nvapiIntf.GpuGetFullName(physicalGpus[0]) << '\n' | |
<< "===LISTING PSTATES===\n" | |
<< "Number of configured performance-states: " << pstates.numPstates; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment