本文已参与「新人创作礼」活动,一起开启掘金创作之路。
作者从processhacker2中抽取gpu监测的代码,剥离出来的命令行工具: windowsGpuMonitor。
流程
process hacker 获取Gpu 数据的具体逻辑主要在
gpumon.c, counter.c中, 因此着重对这两个文件中的一些逻辑进行分析。
main 调用 EtGpuMonitorInitialization 进行初始化
VOID EtGpuMonitorInitialization(
VOID
)
{
// 此处有三个布尔变量控制着是否可以采集,以及采集哪些数据的流程
// 1. EtGpuSupported: 控制着gpu温度数据采集等逻辑
// 2. EtD3DEnabled: 控制着是否通过d3d 的方式获取数据等逻辑
// 3. EtGpuEnabled: 控制着整个采集是否可以正常获取数据
if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GPU_MONITOR))
{
EtGpuSupported = PhWindowsVersion >= WINDOWS_10_RS4;
EtD3DEnabled = EtGpuSupported && !!PhGetIntegerSetting(SETTING_NAME_ENABLE_GPUPERFCOUNTERS);
EtpGpuAdapterList = PhCreateList(4);
// 判断是否可以顺利初始化使用 D3DKMTQueryStatistics 系统API来获取gpu原始数据
if (EtpInitializeD3DStatistics())
EtGpuEnabled = TRUE;
}
if (EtD3DEnabled)
{
// 该函数中分别创建了用于存储一些counter的哈希表
EtPerfCounterInitialization();
}
if (EtGpuEnabled)
{
...
// 注册了回调函数 EtGpuProcessesUpdatedCallback, 该函数中是获取以及更新目前gpu数据的逻辑
PhRegisterCallback(
PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent),
EtGpuProcessesUpdatedCallback,
NULL,
&ProcessesUpdatedCallbackRegistration
);
}
}
EtGpuMonitorInitialization 调用 EtpInitializeD3DStatistics 初始化
BOOLEAN EtpInitializeD3DStatistics(
VOID
)
{
PPH_LIST deviceAdapterList;
PWSTR deviceInterfaceList;
ULONG deviceInterfaceListLength = 0;
PWSTR deviceInterface;
D3DKMT_OPENADAPTERFROMDEVICENAME openAdapterFromDeviceName;
D3DKMT_QUERYSTATISTICS queryStatistics;
D3DKMT_ADAPTER_PERFDATACAPS perfCaps;
// 封装调用了系统 API CM_Get_Device_Interface_List_SizeW, 该API 的作用是提前获取device_list 所需占用的内存大小,存储在deviceInterfaceListLength中,
// 以便提前new 一块内存供存储device_list使用
if (CM_Get_Device_Interface_List_Size(
&deviceInterfaceListLength,
(PGUID)&GUID_DISPLAY_DEVICE_ARRIVAL,
NULL,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT
) != CR_SUCCESS)
{
return FALSE;
}
deviceInterfaceList = PhAllocate(deviceInterfaceListLength * sizeof(WCHAR));
memset(deviceInterfaceList, 0, deviceInterfaceListLength * sizeof(WCHAR));
// 封装了系统 API CM_Get_Device_Interface_ListW, 该API 的作用是获取到多个以 NULL 结尾的字符串, 存储在deviceInterfaceList中,
// 每一个字符串都代表着 the symbolic link name of an interface instance。
if (CM_Get_Device_Interface_List(
(PGUID)&GUID_DISPLAY_DEVICE_ARRIVAL,
NULL,
deviceInterfaceList,
deviceInterfaceListLength,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT
) != CR_SUCCESS)
{
PhFree(deviceInterfaceList);
return FALSE;
}
deviceAdapterList = PhCreateList(10);
// 将deviceInterfaceList 中的多个以NULL结尾的字符串保存在 deviceAdapterList 中
for (deviceInterface = deviceInterfaceList; *deviceInterface; deviceInterface += PhCountStringZ(deviceInterface) + 1)
{
PhAddItemList(deviceAdapterList, deviceInterface);
}
// 通过遍历->打开->判断 每一个deviceAdapterList 中的deviceName, 来判断是否可以通过 D3DKMTQueryStatistics 的方式获取GPU数据
for (ULONG i = 0; i < deviceAdapterList->Count; i++)
{
memset(&openAdapterFromDeviceName, 0, sizeof(D3DKMT_OPENADAPTERFROMDEVICENAME));
openAdapterFromDeviceName.pDeviceName = deviceAdapterList->Items[i];
// 调用系统API D3DKMTOpenAdapterFromDeviceName, 该函数通过 deviceName, 将一个graphics adapter handle 与该deviceName "绑定"
if (!NT_SUCCESS(D3DKMTOpenAdapterFromDeviceName(&openAdapterFromDeviceName)))
continue;
if (EtGpuSupported && deviceAdapterList->Count > 1) // Note: Changed to RS4 due to reports of BSODs on LTSB versions of RS3
{
// 是 software device 就直接关闭该device handle
if (EtpIsGpuSoftwareDevice(openAdapterFromDeviceName.hAdapter))
{
EtCloseAdapterHandle(openAdapterFromDeviceName.hAdapter);
continue;
}
}
// win10 rs4 之后
if (EtGpuSupported) // Note: Changed to RS4 due to reports of BSODs on LTSB versions of RS3
{
D3DKMT_SEGMENTSIZEINFO segmentInfo;
memset(&segmentInfo, 0, sizeof(D3DKMT_SEGMENTSIZEINFO));
// 封装了系统API D3DKMTQueryAdapterInfo, 该函数根据要获取的adapter的数据的类型, 来返回相应的数据
if (NT_SUCCESS(EtQueryAdapterInformation(
openAdapterFromDeviceName.hAdapter,
KMTQAITYPE_GETSEGMENTSIZE,
&segmentInfo,
sizeof(D3DKMT_SEGMENTSIZEINFO)
)))
{
EtGpuDedicatedLimit += segmentInfo.DedicatedVideoMemorySize;
EtGpuSharedLimit += segmentInfo.SharedSystemMemorySize;
}
memset(&perfCaps, 0, sizeof(D3DKMT_ADAPTER_PERFDATACAPS));
if (NT_SUCCESS(EtQueryAdapterInformation(
openAdapterFromDeviceName.hAdapter,
KMTQAITYPE_ADAPTERPERFDATA_CAPS,
&perfCaps,
sizeof(D3DKMT_ADAPTER_PERFDATACAPS)
)))
{
//
// This will be averaged below.
//
EtGpuTemperatureLimit += perfCaps.TemperatureMax;
EtGpuFanRpmLimit += perfCaps.MaxFanRPM;
}
}
// not only w10rs4 , 通过 D3DKMTQueryStatistics 的方式来判断是否可以拿到GPU数据
// D3DKMT_QUERYSTATISTICS_ADAPTER 类型的 D3DKMTQueryStatistics请求, 拿到该adapter 的信息
memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
queryStatistics.Type = D3DKMT_QUERYSTATISTICS_ADAPTER;
queryStatistics.AdapterLuid = openAdapterFromDeviceName.AdapterLuid;
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
{
PETP_GPU_ADAPTER gpuAdapter;
// 提取出一个 PETP_GPU_ADAPTER 的类, 用来保存 GPU 的相关信息
gpuAdapter = EtpAddDisplayAdapter(
openAdapterFromDeviceName.pDeviceName,
openAdapterFromDeviceName.hAdapter,
openAdapterFromDeviceName.AdapterLuid,
queryStatistics.QueryResult.AdapterInformation.NbSegments,
queryStatistics.QueryResult.AdapterInformation.NodeCount
);
// gpu 总的Node数, segemnt数
gpuAdapter->FirstNodeIndex = EtGpuNextNodeIndex;
EtGpuTotalNodeCount += gpuAdapter->NodeCount;
EtGpuTotalSegmentCount += gpuAdapter->SegmentCount;
EtGpuNextNodeIndex += gpuAdapter->NodeCount;
for (ULONG ii = 0; ii < gpuAdapter->SegmentCount; ii++)
{
// D3DKMT_QUERYSTATISTICS 类型的 D3DKMTQueryStatistics请求, 拿到每一个segment 中的数据
memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT;
queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid;
// 此处的segmentId 是用户自定义的数据
queryStatistics.QuerySegment.SegmentId = ii;
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
{
ULONG64 commitLimit;
// 此处的 aperture 其实是一个布尔类型, 其作用是为了判断当前的段是不是 aperture段
ULONG aperture;
if (PhWindowsVersion >= WINDOWS_8)
{
commitLimit = queryStatistics.QueryResult.SegmentInformation.CommitLimit;
aperture = queryStatistics.QueryResult.SegmentInformation.Aperture;
}
else
{
// PD3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1 是 系统 _D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION 结构体的封装
PD3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1 segmentInfo;
segmentInfo = (PD3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1)&queryStatistics.QueryResult;
commitLimit = segmentInfo->CommitLimit;
aperture = segmentInfo->Aperture;
}
if (!EtGpuSupported || !EtD3DEnabled) // Note: Changed to RS4 due to reports of BSODs on LTSB versions of RS3
{
if (aperture)
EtGpuSharedLimit += commitLimit;
else
EtGpuDedicatedLimit += commitLimit;
}
if (aperture)
RtlSetBits(&gpuAdapter->ApertureBitMap, ii, 1);
}
}
}
// 关闭这个 adapter handle
EtCloseAdapterHandle(openAdapterFromDeviceName.hAdapter);
}
...
PhDereferenceObject(deviceAdapterList);
PhFree(deviceInterfaceList);
// 能有GPU Node 则证明可以通过 D3DKMTQueryStatistics 的方式拿到GPU的数据,反正则不能
if (EtGpuTotalNodeCount == 0)
return FALSE;
return TRUE;
}
EtGpuMonitorInitialization 注册回调 EtGpuProcessesUpdatedCallback 获取及更新数据
VOID NTAPI EtGpuProcessesUpdatedCallback(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
)
{
static ULONG runCount = 0; // MUST keep in sync with runCount in process provider
DOUBLE elapsedTime = 0; // total GPU node elapsed time in micro-seconds
FLOAT tempGpuUsage = 0;
ULONG i;
PLIST_ENTRY listEntry;
FLOAT maxNodeValue = 0;
PET_PROCESS_BLOCK maxNodeBlock = NULL;
if (EtD3DEnabled)
{
FLOAT gpuTotal;
ULONG64 dedicatedTotal;
ULONG64 sharedTotal;
// 获取到原始数据并将原始数据存储到相应的 hashtable 和 circularBuffer 中
EtUpdatePerfCounterData();
// 从相应的 hashtable 中拿出原始并计算
gpuTotal = EtLookupTotalGpuUtilization();
dedicatedTotal = EtLookupTotalGpuDedicated();
sharedTotal = EtLookupTotalGpuShared();
if (gpuTotal > 1)
gpuTotal = 1;
if (gpuTotal > tempGpuUsage)
tempGpuUsage = gpuTotal;
EtGpuNodeUsage = tempGpuUsage;
EtGpuDedicatedUsage = dedicatedTotal;
EtGpuSharedUsage = sharedTotal;
}
else
{
// 从segment中拿出内存的数据, 从node中拿出node的运行时长,并根据该数据计算出GPU使用率
EtpUpdateSystemSegmentInformation();
EtpUpdateSystemNodeInformation();
// node的使用率 = node的runtime / elapsedTime
// 使用率 = max(node的使用率)
elapsedTime = (DOUBLE)EtClockTotalRunningTimeDelta.Delta * 10000000 / EtClockTotalRunningTimeFrequency.QuadPart;
if (elapsedTime != 0)
{
for (i = 0; i < EtGpuTotalNodeCount; i++)
{
FLOAT usage = (FLOAT)(EtGpuNodesTotalRunningTimeDelta[i].Delta / elapsedTime);
// 大于1按1算
if (usage > 1)
usage = 1;
// max(node_usage)
if (usage > tempGpuUsage)
tempGpuUsage = usage;
}
}
EtGpuNodeUsage = tempGpuUsage;
}
if (EtGpuSupported && EtpGpuAdapterList->Count) {
...
}
// Update per-process statistics.
// Note: no lock is needed because we only ever modify the list on this same thread.
listEntry = EtProcessBlockListHead.Flink;
// 更新每一个进程所使用GPU的信息
while (listEntry != &EtProcessBlockListHead)
{
PET_PROCESS_BLOCK block;
block = CONTAINING_RECORD(listEntry, ET_PROCESS_BLOCK, ListEntry);
if (block->ProcessItem->State & PH_PROCESS_ITEM_REMOVED)
{
listEntry = listEntry->Flink;
continue;
}
if (EtD3DEnabled)
{
// 依据processId 在相应的HashTable 中找到所需的数据
ULONG64 sharedUsage;
ULONG64 dedicatedUsage;
ULONG64 commitUsage;
block->GpuNodeUtilization = EtLookupProcessGpuUtilization(block->ProcessItem->ProcessId);
if (EtLookupProcessGpuMemoryCounters(
block->ProcessItem->ProcessId,
&sharedUsage,
&dedicatedUsage,
&commitUsage
))
{
block->GpuSharedUsage = sharedUsage;
block->GpuDedicatedUsage = dedicatedUsage;
block->GpuCommitUsage = commitUsage;
}
else
{
block->GpuSharedUsage = 0;
block->GpuDedicatedUsage = 0;
block->GpuCommitUsage = 0;
}
...
}
else
{
// EtpUpdateProcessSegmentInformation 中设置了该进程所占GPU内存的数据
EtpUpdateProcessSegmentInformation(block);
EtpUpdateProcessNodeInformation(block);
if (elapsedTime != 0)
{
// 对于每一个进程而言, 其node的使用率为:
// node_usage = process_runningtime / elapsedTime
block->GpuNodeUtilization = (FLOAT)(block->GpuRunningTimeDelta.Delta / elapsedTime);
// HACK
// 单个进程的使用率不能超过总的使用率
if (block->GpuNodeUtilization > EtGpuNodeUsage)
block->GpuNodeUtilization = EtGpuNodeUsage;
//for (i = 0; i < EtGpuTotalNodeCount; i++)
//{
// FLOAT usage = (FLOAT)(block->GpuTotalRunningTimeDelta[i].Delta / elapsedTime);
//
// if (usage > block->GpuNodeUtilization)
// {
// block->GpuNodeUtilization = usage;
// }
//}
// 使用率最大为1
if (block->GpuNodeUtilization > 1)
block->GpuNodeUtilization = 1;
if (runCount != 0)
{
block->CurrentGpuUsage = block->GpuNodeUtilization;
block->CurrentMemUsage = (ULONG)(block->GpuDedicatedUsage / PAGE_SIZE);
block->CurrentMemSharedUsage = (ULONG)(block->GpuSharedUsage / PAGE_SIZE);
block->CurrentCommitUsage = (ULONG)(block->GpuCommitUsage / PAGE_SIZE);
...
}
}
}
if (maxNodeValue < block->GpuNodeUtilization)
{
maxNodeValue = block->GpuNodeUtilization;
maxNodeBlock = block;
}
listEntry = listEntry->Flink;
}
// Update history buffers.
if (runCount != 0)
{
...
}
runCount++;
}
可以看到在
EtGpuProcessesUpdatedCallback中存在两种获取数据的逻辑,展开如下。
EtD3DEnable == true
graph TD
PerfQueryCounterData[PerfQueryCounterData] -->|buffer| EtPerfCounterGetCounterData(EtPerfCounterGetCounterData)
EtPerfCounterGetCounterData[EtPerfCounterGetCounterData] -->|perfQueryBuffer|EtUpdatePerfCounterData(EtUpdatePerfCounterData)
EtUpdatePerfCounterData[EtUpdatePerfCounterData] -->|counter| EtPerfCounterProcessGpuEngineUtilizationCounter(EtPerfCounterProcessGpuEngineUtilizationCounter)
EtUpdatePerfCounterData[EtUpdatePerfCounterData] -->|counter|EtPerfCounterGpuProcessUtilizationCounter(EtPerfCounterGpuProcessUtilizationCounter)
EtUpdatePerfCounterData[EtUpdatePerfCounterData] -->|counter|EtPerfCounterGpuAdapterDedicatedCounter(EtPerfCounterGpuAdapterDedicatedCounter)
EtPerfCounterProcessGpuEngineUtilizationCounter[EtPerfCounterProcessGpuEngineUtilizationCounter]-.->
EtGpuRunningTimeHashTable(EtGpuRunningTimeHashTable)
EtPerfCounterGpuProcessUtilizationCounter[EtPerfCounterGpuProcessUtilizationCounter]-.->
EtGpuAdapterDedicatedHashTable(EtGpuAdapterDedicatedHashTable)
EtPerfCounterGpuAdapterDedicatedCounter[EtPerfCounterGpuAdapterDedicatedCounter]-.->
EtGpuProcessCounterHashTable(EtGpuProcessCounterHashTable)
EtGpuRunningTimeHashTable(EtGpuRunningTimeHashTable) --> EtLookupTotalGpuUtilization(EtLookupTotalGpuUtilization)
EtGpuAdapterDedicatedHashTable(EtGpuAdapterDedicatedHashTable) -->
EtLookupTotalGpuDedicated(EtLookupTotalGpuDedicated)
EtGpuProcessCounterHashTable(EtGpuProcessCounterHashTable) -->
EtLookupTotalGpuShared(EtLookupTotalGpuShared)
EtLookupTotalGpuUtilization(EtLookupTotalGpuUtilization) -->
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)
EtLookupTotalGpuDedicated(EtLookupTotalGpuDedicated) -->
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)
EtLookupTotalGpuShared(EtLookupTotalGpuShared) -->
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)
从原始数据到最后被monitor 拿到的数据:
- EtPerfCounterGetCounterData 调用系统API PerfQueryCounterData 拿到原始数据,并将原始数据存入 buffer中:
_Success_(return)
BOOLEAN EtPerfCounterGetCounterData(
_In_ HANDLE CounterHandle,
_Out_ PPERF_DATA_HEADER *CounterBuffer
)
{
...
buffer = PhAllocate(bufferSize);
status = PerfQueryCounterData(
CounterHandle,
buffer,
bufferSize,
&bufferSize
);
if (status == ERROR_NOT_ENOUGH_MEMORY)
{
if (initialBufferSize < bufferSize)
initialBufferSize = bufferSize;
PhFree(buffer);
buffer = PhAllocate(bufferSize);
status = PerfQueryCounterData(
CounterHandle,
buffer,
bufferSize,
&bufferSize
);
}
...
}
- EtUpdatePerfCounterData 通过偏移拿取
PPERF_COUNTER_HEADER, PPERF_DATA_HEADER等数据,再把这些数据进行分类,将不同类型的数据放入不同的counter中。 EtPerfCounterGpuAdapterDedicatedCounter, EtPerfCounterGpuProcessUtilizationCounter, EtPerfCounterProcessGpuEngineUtilizationCounter函数,将counter的数据存储到该类型counter 相对应的 hashtable 中。- EtGpuProcessesUpdatedCallback 获取gpu相关数据时,调用
EtLookupTotalGpuUtilization, EtLookupTotalGpuDedicated, EtLookupTotalGpuShared方法,从每一个 counter 相对应的 hashtable 中拿到数据。
EtD3DEnable == false
graph TD
D3DKMTQueryStatistics[D3DKMTQueryStatistics] -->|D3DKMT_QUERYSTATISTICS_SEGMENT|EtpUpdateSystemSegmentInformation(EtpUpdateSystemSegmentInformation)
D3DKMTQueryStatistics[D3DKMTQueryStatistics] -->|D3DKMT_QUERYSTATISTICS_NODE|EtpUpdateSystemNodeInformation(EtpUpdateSystemNodeInformation)
EtpUpdateSystemSegmentInformation[EtpUpdateSystemSegmentInformation]-.->EtGpuDedicatedUsage(EtGpuDedicatedUsage)
EtpUpdateSystemSegmentInformation[EtpUpdateSystemSegmentInformation]-.->EtGpuSharedUsage(EtGpuSharedUsage)
EtpUpdateSystemNodeInformation[EtpUpdateSystemNodeInformation]-.->
EtGpuNodesTotalRunningTimeDelta(EtGpuNodesTotalRunningTimeDelta)
EtGpuDedicatedUsage(EtGpuDedicatedUsage) -->
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)
EtGpuSharedUsage(EtGpuSharedUsage) -->
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)
EtGpuNodesTotalRunningTimeDelta(EtGpuNodesTotalRunningTimeDelta) -->
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)
EtGpuProcessesUpdatedCallback 调用 EtpUpdateSystemSegmentInformation 获取以及更新GPU内存数据
VOID EtpUpdateSystemSegmentInformation(
VOID
)
{
ULONG i;
ULONG j;
PETP_GPU_ADAPTER gpuAdapter;
D3DKMT_QUERYSTATISTICS queryStatistics;
ULONG64 dedicatedUsage;
ULONG64 sharedUsage;
dedicatedUsage = 0;
sharedUsage = 0;
// 遍历每一个可用的 gpu adapter
for (i = 0; i < EtpGpuAdapterList->Count; i++)
{
gpuAdapter = EtpGpuAdapterList->Items[i];
// 遍历每一个 gpu adapter 的每一个segment
for (j = 0; j < gpuAdapter->SegmentCount; j++)
{
memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT;
queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid;
queryStatistics.QuerySegment.SegmentId = j;
// 发送 D3DKMT_QUERYSTATISTICS_SEGMENT 类型的 D3DKMTQueryStatistics 请求, 拿到该段的内存数据
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
{
ULONG64 bytesCommitted;
ULONG aperture;
if (PhWindowsVersion >= WINDOWS_8)
{
bytesCommitted = queryStatistics.QueryResult.SegmentInformation.BytesResident;
aperture = queryStatistics.QueryResult.SegmentInformation.Aperture;
}
else
{
PD3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1 segmentInfo;
segmentInfo = (PD3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1)&queryStatistics.QueryResult;
bytesCommitted = segmentInfo->BytesResident;
aperture = segmentInfo->Aperture;
}
if (aperture) // RtlCheckBit(&gpuAdapter->ApertureBitMap, j)
sharedUsage += bytesCommitted;
else
dedicatedUsage += bytesCommitted;
}
}
}
EtGpuDedicatedUsage = dedicatedUsage;
EtGpuSharedUsage = sharedUsage;
}
EtGpuProcessesUpdatedCallback 调用 EtpUpdateSystemNodeInformation 获取以及更新node数据
VOID EtpUpdateSystemNodeInformation(
VOID
)
{
PETP_GPU_ADAPTER gpuAdapter;
D3DKMT_QUERYSTATISTICS queryStatistics;
LARGE_INTEGER performanceCounter;
for (ULONG i = 0; i < EtpGpuAdapterList->Count; i++)
{
gpuAdapter = EtpGpuAdapterList->Items[i];
for (ULONG j = 0; j < gpuAdapter->NodeCount; j++)
{
memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
queryStatistics.Type = D3DKMT_QUERYSTATISTICS_NODE;
queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid;
queryStatistics.QueryNode.NodeId = j;
// 发送类型为 D3DKMT_QUERYSTATISTICS_NODE 的 D3DKMTQueryStatistics 请求, 拿到该node 的runtime 数据
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
{
ULONG64 runningTime;
//ULONG64 systemRunningTime;
runningTime = queryStatistics.QueryResult.NodeInformation.GlobalInformation.RunningTime.QuadPart;
//systemRunningTime = queryStatistics.QueryResult.NodeInformation.SystemInformation.RunningTime.QuadPart;
// 更新每一个 每一个gpuadapter的每一个node 的runningTime
PhUpdateDelta(&EtGpuNodesTotalRunningTimeDelta[gpuAdapter->FirstNodeIndex + j], runningTime);
}
}
}
PhQueryPerformanceCounter(&performanceCounter, &EtClockTotalRunningTimeFrequency);
PhUpdateDelta(&EtClockTotalRunningTimeDelta, performanceCounter.QuadPart);
}