ADLを使ってRadeonのGPU Clockなどを読む


## はじめに
[AMD Display Library SDK](https://github.com/GPUOpen-LibrariesAndSDKs/display-library) を使い、GPU-Zにある次の値に近いものを読み取ってみます。
* GPU Clock
* Memory Clock
* GPU Temperature
* Fan Speed (%)
* Fan Speed (RPM)
* GPU Load
* GPU Only Power Draw
* VDDC

動作を確認した環境は、
* ADL Version 12.0
* Radeon Software Version 19.5.1
* Microsoft Windows [Version 10.0.17763.503] X64
* Radeon RX 580
です。

## わずかな注意点
プレフィクスが**ADL**のものと**ADL2**のAPIがあります。後者はコンテキストの概念があり複数の状態を持てますが前者はできません。一部APIでは後者のみ実装されているものがあるので**ADL2**のものを使うのが良いでしょう。  
**ADL{2}_Main_Control{X2}_Create**でメモリ割り当ての関数のポインタを渡します。今回は渡したポインタが指す関数が呼ばれることはありませんが、ライブラリ側で動的にメモリを割り当てて、後から利用者が開放するケースで呼ばれます。

## 利用例
```C++:main.cpp
#include <Windows.h>
#include <cstdio>
#include <ADL\include\adl_sdk.h>

using ADL2_Main_ControlX2_Create_type = int (*)(ADL_MAIN_MALLOC_CALLBACK, int, ADL_CONTEXT_HANDLE *, ADLThreadingModel);
using ADL2_Main_Control_Destroy_type = int (*)(ADL_CONTEXT_HANDLE);
using ADL2_Adapter_NumberOfAdapters_Get_type = int (*)(ADL_CONTEXT_HANDLE, int *);
using ADL2_Adapter_AdapterInfo_Get_type = int (*)(ADL_CONTEXT_HANDLE, LPAdapterInfo, int);
using ADL2_Adapter_Active_Get_type = int (*)(ADL_CONTEXT_HANDLE, int, int *);
using ADL2_Overdrive_Caps_type = int (*)(ADL_CONTEXT_HANDLE, int, int *, int *, int *);
using ADL2_OverdriveN_PerformanceStatus_Get_type = int (*)(ADL_CONTEXT_HANDLE, int, ADLODNPerformanceStatus *);
using ADL2_OverdriveN_Temperature_Get_type = int (*)(ADL_CONTEXT_HANDLE, int, int, int *);
using ADL2_Overdrive5_FanSpeed_Get_type = int (*)(ADL_CONTEXT_HANDLE, int, int, ADLFanSpeedValue *);
using ADL2_Overdrive6_CurrentPower_Get_type = int (*)(ADL_CONTEXT_HANDLE, int, int, int *);

ADL2_Main_ControlX2_Create_type ADL2_Main_ControlX2_Create;
ADL2_Main_Control_Destroy_type ADL2_Main_Control_Destroy;
ADL2_Adapter_NumberOfAdapters_Get_type ADL2_Adapter_NumberOfAdapters_Get;
ADL2_Adapter_AdapterInfo_Get_type ADL2_Adapter_AdapterInfo_Get;
ADL2_Adapter_Active_Get_type ADL2_Adapter_Active_Get;
ADL2_Overdrive_Caps_type ADL2_Overdrive_Caps;
ADL2_OverdriveN_PerformanceStatus_Get_type ADL2_OverdriveN_PerformanceStatus_Get;
ADL2_OverdriveN_Temperature_Get_type ADL2_OverdriveN_Temperature_Get;
ADL2_Overdrive5_FanSpeed_Get_type ADL2_Overdrive5_FanSpeed_Get;
ADL2_Overdrive6_CurrentPower_Get_type ADL2_Overdrive6_CurrentPower_Get;

void * __stdcall ADL_Main_Memory_Alloc(int size)
{
	return malloc(size);
}

int main()
{
	ADL_CONTEXT_HANDLE ctx = nullptr;
	HMODULE adl_handle = LoadLibraryA("C:/Windows/System32/atiadlxx.dll");

	ADL2_Main_ControlX2_Create = reinterpret_cast<decltype(ADL2_Main_ControlX2_Create)>(GetProcAddress(adl_handle, "ADL2_Main_ControlX2_Create"));
	ADL2_Main_Control_Destroy = reinterpret_cast<decltype(ADL2_Main_Control_Destroy)>(GetProcAddress(adl_handle, "ADL2_Main_Control_Destroy"));
	ADL2_Adapter_NumberOfAdapters_Get = reinterpret_cast<decltype(ADL2_Adapter_NumberOfAdapters_Get)>(GetProcAddress(adl_handle, "ADL2_Adapter_NumberOfAdapters_Get"));
	ADL2_Adapter_AdapterInfo_Get = reinterpret_cast<decltype(ADL2_Adapter_AdapterInfo_Get)>(GetProcAddress(adl_handle, "ADL2_Adapter_AdapterInfo_Get"));
	ADL2_Adapter_Active_Get = reinterpret_cast<decltype(ADL2_Adapter_Active_Get)>(GetProcAddress(adl_handle, "ADL2_Adapter_Active_Get"));
	ADL2_Overdrive_Caps = reinterpret_cast<decltype(ADL2_Overdrive_Caps)>(GetProcAddress(adl_handle, "ADL2_Overdrive_Caps"));
	ADL2_OverdriveN_PerformanceStatus_Get = reinterpret_cast<decltype(ADL2_OverdriveN_PerformanceStatus_Get)>(GetProcAddress(adl_handle, "ADL2_OverdriveN_PerformanceStatus_Get"));
	ADL2_OverdriveN_Temperature_Get = reinterpret_cast<decltype(ADL2_OverdriveN_Temperature_Get)>(GetProcAddress(adl_handle, "ADL2_OverdriveN_Temperature_Get"));
	ADL2_Overdrive5_FanSpeed_Get = reinterpret_cast<decltype(ADL2_Overdrive5_FanSpeed_Get)>(GetProcAddress(adl_handle, "ADL2_Overdrive5_FanSpeed_Get"));
	ADL2_Overdrive6_CurrentPower_Get = reinterpret_cast<decltype(ADL2_Overdrive6_CurrentPower_Get)>(GetProcAddress(adl_handle, "ADL2_Overdrive6_CurrentPower_Get"));

	ADL2_Main_ControlX2_Create(ADL_Main_Memory_Alloc, 0, &ctx, ADL_THREADING_UNLOCKED);

	int adapters = 0;
	ADL2_Adapter_NumberOfAdapters_Get(ctx, &adapters);

	LPAdapterInfo adapter_info = new AdapterInfo[adapters];
	memset(adapter_info, 0, sizeof (AdapterInfo) * adapters);
	ADL2_Adapter_AdapterInfo_Get(ctx, adapter_info, sizeof (AdapterInfo) * adapters);

	for (int i=0; i<adapters; i++)
	{
		int adapter_index = adapter_info[i].iAdapterIndex;
		int is_active_adapter = ADL_FALSE;
		int overdrive_supported = ADL_FALSE;
		int overdrive_enabled = ADL_FALSE;
		int overdrive_version = -1;

		ADL2_Adapter_Active_Get(ctx, adapter_index, &is_active_adapter);
		if (ADL_TRUE != is_active_adapter)
			continue;

		ADL2_Overdrive_Caps(ctx, adapter_index, &overdrive_supported, &overdrive_enabled, &overdrive_version);
		if (ADL_TRUE!=overdrive_supported || ADL_TRUE!=overdrive_enabled || 7>overdrive_version)
			continue;
		
		ADLODNPerformanceStatus performance_status = {};
		int celsius_temperature = 0;
		ADLFanSpeedValue percent_fan_speed_info = {};
		ADLFanSpeedValue rpm_fan_speed_info = {};
		int watts = 0;
		
		ADL2_OverdriveN_PerformanceStatus_Get(ctx, adapter_index, &performance_status);

		ADL2_OverdriveN_Temperature_Get(ctx, adapter_index, 1, &celsius_temperature);

		percent_fan_speed_info.iSize = sizeof (percent_fan_speed_info);
		percent_fan_speed_info.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_PERCENT;
		ADL2_Overdrive5_FanSpeed_Get(ctx, adapter_index, 0, &percent_fan_speed_info);

		rpm_fan_speed_info.iSize = sizeof (rpm_fan_speed_info);
		rpm_fan_speed_info.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_RPM;
		ADL2_Overdrive5_FanSpeed_Get(ctx, adapter_index, 0, &rpm_fan_speed_info);

		ADL2_Overdrive6_CurrentPower_Get(ctx, adapter_index, 0, &watts);

		std::printf("GPU Clock: %.1f MHz\n", performance_status.iCoreClock / 100.0f);
		std::printf("Memory Clock: %.1f MHz\n", performance_status.iMemoryClock / 100.0f);
		std::printf("GPU Temperature: %.1f ℃\n", celsius_temperature / 1000.0f);
		std::printf("Fan Speed (%%): %d %%\n", percent_fan_speed_info.iFanSpeed);
		std::printf("Fan Speed (RPM): %d RPM\n", rpm_fan_speed_info.iFanSpeed);
		std::printf("GPU Load: %d\n", performance_status.iGPUActivityPercent);
		std::printf("GPU only Power Draw: %.1f W\n", watts / 256.0f);
		std::printf("VDDC: %.3f V\n", performance_status.iVDDC / 1000.0f);
	}

	delete[] adapter_info;
	ADL2_Main_Control_Destroy(ctx);
	FreeLibrary(adl_handle);

	return 0;
}
```