NVAPIって知っていますか?NVAPIとはNVIDIA社が提供しているAPIです。NVIDIA社が提供している有名なAPIとしてはCUDAが存在しますが、CUDAとは違いNVAPIは搭載されているハードウェアの情報を取得することが主な使用用途だと思われます。
さて、私はGPUを酷使する機会が多少ながらもあり、その時にVRAMがどの程度使用されているかどうかを取得するためのNVAPIのコードを紹介します。
NVAPIをダウンロード
まずは、NVAPIを以下のリンクからダウンロードしてきます。今回この記事で利用しているNVAPIのバージョンは「R450」です。
ダウンロードが終えたファイルの中には「nvapi.h」などのヘッタファイルがあり、ライブラリが入っているファイルがあることが分かります。NVAPIファイルを、プログラムを作成する作業フォルダ内に配置してください。
VRAMの取得を行うコード
#include <stdio.h>
#include "NVAPI/nvapi.h"
#if _M_AMD64
#pragma comment(lib, "NVAPI/amd64/nvapi64.lib")
#else
#pragma comment(lib, "NVAPI/x86/nvapi.lib")
#endif
int main() {
NvAPI_Status nStatus = NvAPI_Initialize();
if (nStatus == NVAPI_OK) {
NvPhysicalGpuHandle hGPUHandles[NVAPI_MAX_PHYSICAL_GPUS] = {0};
NvU32 count = 0;
nStatus = NvAPI_EnumPhysicalGPUs(hGPUHandles, &count);
if (nStatus == NVAPI_OK) {
printf("GPUS:%d\n", count);
} else {
printf("ErrorCode:%d\n", nStatus);
}
NvPhysicalGpuHandle hGPUHandle = hGPUHandles[0];
NV_DISPLAY_DRIVER_MEMORY_INFO_V1 MemoryInfo;
MemoryInfo.version = NV_DISPLAY_DRIVER_MEMORY_INFO_VER_1;
nStatus = NvAPI_GPU_GetMemoryInfo(hGPUHandle, &MemoryInfo);
if (nStatus == NVAPI_OK) {
printf("VRAM Total:%.5lf[GB]\n",
(double)MemoryInfo.dedicatedVideoMemory / 1048576);
printf("VRAM Can Use:%.5lf[GB]\n",
(double)MemoryInfo.availableDedicatedVideoMemory / 1048576);
} else {
printf("ErrorCode:%d\n", nStatus);
}
}
return 0;
}
今回使用するコンパイラはMSVC(cl.exe)を使用します。clangコンパイラでもコンパイル可能です。
コードの説明
#include <stdio.h>
#include "NVAPI/nvapi.h"
#if _M_AMD64
#pragma comment(lib, "NVAPI/amd64/nvapi64.lib")
#else
#pragma comment(lib, "NVAPI/x86/nvapi.lib")
#endif
まず、標準出力のための「stdio.h」VRAMを取得するための「nvapi.h」を追加します。
「nvapi.h」の下部にわけわからんコードになっていますが、これはおまじないだと思ってコピーしてください。この部分ではコンパイラが32ビット版を使用してコンパイルを行うか64ビット版のコンパイラを使用してコンパイルを行うかの違いです。深く考えない方が良いでしょう()
NvAPI_Status nStatus = NvAPI_Initialize();
if (nRet == NVAPI_OK) {
NvPhysicalGpuHandle hGPUHandles[NVAPI_MAX_PHYSICAL_GPUS] = {0};
NvU32 count = 0;
nStatus = NvAPI_EnumPhysicalGPUs(hGPUHandles, &count);
if (nStatus == NVAPI_OK) {
printf("GPUS:%d\n", count);
} else {
printf("ErrorCode:%d\n", nStatus);
}
NvPhysicalGpuHandle hGPUHandle = hGPUHandles[0];//下に続く
ここから本題です。まず、NvAPI_Initialize関数を呼び出して初期化を行います。この際、NvAPI_Initialize関数がNvAPI_Statusであるためステータスコードを取得しておくと後からエラーが出た時に対処がしやすいです。
無事に初期化が行われれば、NVAPI_OKステータスが戻ってくるので、if文で判別してエラーが無いことを確認できます。つぎにGPU自体がコンピュータに何台搭載されていて、どのGPUのVRAMを取得したのかを選択します。
そして、NvPhysicalGpuHandle hGPUHandles[NVAPI_MAX_PHYSICAL_GPUS] = {0};で複数のGPUハンドルが格納するための変数を作ります。NvAPI_EnumPhysicalGPUs関数を使用してGPUハンドルを取得します。後に、NvPhysicalGpuHandle hGPUHandle = hGPUHandles[0];を行うことにより、GPU0のハンドルが変数に格納されて、ようやくVRAMを取得できるような形となります。
NV_DISPLAY_DRIVER_MEMORY_INFO_V1 MemoryInfo;
MemoryInfo.version = NV_DISPLAY_DRIVER_MEMORY_INFO_VER_1;
nStatus = NvAPI_GPU_GetMemoryInfo(hGPUHandle, &MemoryInfo);
if (nStatus == NVAPI_OK) {
printf("VRAM Total:%.5lf[GB]\n",
(double)MemoryInfo.dedicatedVideoMemory / 1048576);
printf("VRAM Can Use:%.5lf[GB]\n",
(double)MemoryInfo.availableDedicatedVideoMemory / 1048576);
} else {
printf("ErrorCode:%d\n", nStatus);
}
まず、使用する構造体を変数宣言(MemoryInfoのこと)を行い、MemoryInfo.version = NV_DISPLAY_DRIVER_MEMORY_INFO_VER_1;で使用する構造体のドライバを当てます。そして、やっとのことでVRAMを取得するための関数が呼び出されます。NvAPI_GPU_GetMemoryInfo(hGPUHandle, &MemoryInfo);この関数ではGPUのハンドルと変数を引数として渡します。最後にこの構造体の値はすべてKBで取得されるためGBに変換して出力しています。
出力結果
GPUS:1
VRAM Total:8.00000[GB]
VRAM Can Use:7.89844[GB]
私のパソコンに搭載しているグラフィックボードはASUS製のGTX1070-8G 1機だけなのGPUSは1と表示され、Totalの所が8GBとなり、Can Useの所は使用可能量となっているので出力結果はリソースの使用状況によって変わります。