Process Hacker
gpunodes.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Extended Tools -
3  * GPU nodes window
4  *
5  * Copyright (C) 2011-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "exttools.h"
24 #include "resource.h"
25 #include <windowsx.h>
26 #include <math.h>
27 
28 #define UPDATE_MSG (WM_APP + 1)
29 #define GRAPH_PADDING 5
30 #define CHECKBOX_PADDING 3
31 
32 INT_PTR CALLBACK EtpGpuNodesDlgProc(
33  _In_ HWND hwndDlg,
34  _In_ UINT uMsg,
35  _In_ WPARAM wParam,
36  _In_ LPARAM lParam
37  );
38 
39 static HWND WindowHandle;
40 static RECT MinimumSize;
41 static PH_LAYOUT_MANAGER LayoutManager;
42 static RECT LayoutMargin;
43 static HWND *GraphHandle;
44 static HWND *CheckBoxHandle;
45 static PPH_GRAPH_STATE GraphState;
46 static PPH_SYSINFO_PARAMETERS SysInfoParameters;
47 static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration;
48 
50  _In_ HWND ParentWindowHandle,
51  _In_ PPH_SYSINFO_PARAMETERS Parameters
52  )
53 {
54  SysInfoParameters = Parameters;
55  DialogBox(
57  MAKEINTRESOURCE(IDD_GPUNODES),
58  ParentWindowHandle,
60  );
61 }
62 
64  _In_opt_ PVOID Parameter,
65  _In_opt_ PVOID Context
66  )
67 {
68  PostMessage(WindowHandle, UPDATE_MSG, 0, 0);
69 }
70 
72  VOID
73  )
74 {
75  ULONG i;
76 
77  for (i = 0; i < EtGpuTotalNodeCount; i++)
78  {
79  Button_SetCheck(
80  CheckBoxHandle[i],
81  RtlCheckBit(&EtGpuNodeBitMap, i) ? BST_CHECKED : BST_UNCHECKED
82  );
83  }
84 }
85 
87  VOID
88  )
89 {
90  RTL_BITMAP newBitMap;
91  ULONG i;
92 
93  EtAllocateGpuNodeBitMap(&newBitMap);
94 
95  for (i = 0; i < EtGpuTotalNodeCount; i++)
96  {
97  if (Button_GetCheck(CheckBoxHandle[i]) == BST_CHECKED)
98  RtlSetBits(&newBitMap, i, 1);
99  }
100 
101  if (RtlNumberOfSetBits(&newBitMap) == 0)
102  RtlSetBits(&newBitMap, 0, 1);
103 
104  EtUpdateGpuNodeBitMap(&newBitMap);
105 }
106 
107 INT_PTR CALLBACK EtpGpuNodesDlgProc(
108  _In_ HWND hwndDlg,
109  _In_ UINT uMsg,
110  _In_ WPARAM wParam,
111  _In_ LPARAM lParam
112  )
113 {
114  switch (uMsg)
115  {
116  case WM_INITDIALOG:
117  {
118  ULONG i;
119  HFONT font;
120  PPH_STRING nodeString;
121  RECT labelRect;
122  RECT tempRect;
123  ULONG numberOfRows;
124  ULONG numberOfColumns;
125 
126  WindowHandle = hwndDlg;
127  PhCenterWindow(hwndDlg, GetParent(hwndDlg));
128 
129  PhInitializeLayoutManager(&LayoutManager, hwndDlg);
130  PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
131  LayoutMargin = PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_LAYOUT), NULL, PH_ANCHOR_ALL)->Margin;
132 
133  PhRegisterCallback(&PhProcessesUpdatedEvent, ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration);
134 
135  GraphHandle = PhAllocate(sizeof(HWND) * EtGpuTotalNodeCount);
136  CheckBoxHandle = PhAllocate(sizeof(HWND) * EtGpuTotalNodeCount);
137  GraphState = PhAllocate(sizeof(PH_GRAPH_STATE) * EtGpuTotalNodeCount);
138 
139  font = (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0);
140 
141  for (i = 0; i < EtGpuTotalNodeCount; i++)
142  {
143  nodeString = PhFormatString(L"Node %lu", i);
144 
145  GraphHandle[i] = CreateWindow(
147  NULL,
148  WS_VISIBLE | WS_CHILD | WS_BORDER,
149  0,
150  0,
151  3,
152  3,
153  hwndDlg,
154  NULL,
155  NULL,
156  NULL
157  );
158  Graph_SetTooltip(GraphHandle[i], TRUE);
159  CheckBoxHandle[i] = CreateWindow(
160  WC_BUTTON,
161  nodeString->Buffer,
162  WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX,
163  0,
164  0,
165  3,
166  3,
167  hwndDlg,
168  NULL,
169  NULL,
170  NULL
171  );
172  SendMessage(CheckBoxHandle[i], WM_SETFONT, (WPARAM)font, FALSE);
173  PhInitializeGraphState(&GraphState[i]);
174 
175  PhDereferenceObject(nodeString);
176  }
177 
178  // Calculate the minimum size.
179 
180  numberOfRows = (ULONG)sqrt(EtGpuTotalNodeCount);
181  numberOfColumns = (EtGpuTotalNodeCount + numberOfRows - 1) / numberOfRows;
182 
183  MinimumSize.left = 0;
184  MinimumSize.top = 0;
185  MinimumSize.right = 45;
186  MinimumSize.bottom = 60;
187  MapDialogRect(hwndDlg, &MinimumSize);
188  MinimumSize.right += (MinimumSize.right + GRAPH_PADDING) * numberOfColumns;
189  MinimumSize.bottom += (MinimumSize.bottom + GRAPH_PADDING) * numberOfRows;
190 
191  GetWindowRect(GetDlgItem(hwndDlg, IDC_INSTRUCTION), &labelRect);
192  MapWindowPoints(NULL, hwndDlg, (POINT *)&labelRect, 2);
193  labelRect.right += GetSystemMetrics(SM_CXFRAME) * 2;
194 
195  tempRect.left = 0;
196  tempRect.top = 0;
197  tempRect.right = 7;
198  tempRect.bottom = 0;
199  MapDialogRect(hwndDlg, &tempRect);
200  labelRect.right += tempRect.right;
201 
202  if (MinimumSize.right < labelRect.right)
203  MinimumSize.right = labelRect.right;
204 
205  SetWindowPos(hwndDlg, NULL, 0, 0, MinimumSize.right, MinimumSize.bottom, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
206 
208  }
209  break;
210  case WM_DESTROY:
211  {
212  ULONG i;
213 
215 
216  PhUnregisterCallback(&PhProcessesUpdatedEvent, &ProcessesUpdatedCallbackRegistration);
217 
218  for (i = 0; i < EtGpuTotalNodeCount; i++)
219  {
220  PhDeleteGraphState(&GraphState[i]);
221  }
222 
223  PhFree(GraphHandle);
224  PhFree(CheckBoxHandle);
225  PhFree(GraphState);
226 
227  PhDeleteLayoutManager(&LayoutManager);
228  }
229  break;
230  case WM_SIZE:
231  {
232  HDWP deferHandle;
233  RECT clientRect;
234  RECT checkBoxRect;
235  ULONG numberOfRows = (ULONG)sqrt(EtGpuTotalNodeCount);
236  ULONG numberOfColumns = (EtGpuTotalNodeCount + numberOfRows - 1) / numberOfRows;
237  ULONG numberOfYPaddings = numberOfRows - 1;
238  ULONG numberOfXPaddings = numberOfColumns - 1;
239  ULONG cellHeight;
240  ULONG y;
241  ULONG cellWidth;
242  ULONG x;
243  ULONG i;
244 
245  PhLayoutManagerLayout(&LayoutManager);
246 
247  deferHandle = BeginDeferWindowPos(EtGpuTotalNodeCount * 2);
248 
249  GetClientRect(hwndDlg, &clientRect);
250  GetClientRect(GetDlgItem(hwndDlg, IDC_EXAMPLE), &checkBoxRect);
251  cellHeight = (clientRect.bottom - LayoutMargin.top - LayoutMargin.bottom - GRAPH_PADDING * numberOfYPaddings) / numberOfRows;
252  y = LayoutMargin.top;
253  i = 0;
254 
255  for (ULONG row = 0; row < numberOfRows; ++row)
256  {
257  // Give the last row the remaining space; the height we calculated might be off by a few
258  // pixels due to integer division.
259  if (row == numberOfRows - 1)
260  cellHeight = clientRect.bottom - LayoutMargin.bottom - y;
261 
262  cellWidth = (clientRect.right - LayoutMargin.left - LayoutMargin.right - GRAPH_PADDING * numberOfXPaddings) / numberOfColumns;
263  x = LayoutMargin.left;
264 
265  for (ULONG column = 0; column < numberOfColumns; column++)
266  {
267  // Give the last cell the remaining space; the width we calculated might be off by a few
268  // pixels due to integer division.
269  if (column == numberOfColumns - 1)
270  cellWidth = clientRect.right - LayoutMargin.right - x;
271 
272  if (i < EtGpuTotalNodeCount)
273  {
274  deferHandle = DeferWindowPos(
275  deferHandle,
276  GraphHandle[i],
277  NULL,
278  x,
279  y,
280  cellWidth,
281  cellHeight - checkBoxRect.bottom - CHECKBOX_PADDING,
282  SWP_NOACTIVATE | SWP_NOZORDER
283  );
284  deferHandle = DeferWindowPos(
285  deferHandle,
286  CheckBoxHandle[i],
287  NULL,
288  x,
289  y + cellHeight - checkBoxRect.bottom,
290  cellWidth,
291  checkBoxRect.bottom,
292  SWP_NOACTIVATE | SWP_NOZORDER
293  );
294  i++;
295  }
296  x += cellWidth + GRAPH_PADDING;
297  }
298 
299  y += cellHeight + GRAPH_PADDING;
300  }
301 
302  EndDeferWindowPos(deferHandle);
303  }
304  break;
305  case WM_SIZING:
306  {
307  PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom);
308  }
309  break;
310  case WM_COMMAND:
311  {
312  switch (LOWORD(wParam))
313  {
314  case IDCANCEL:
315  case IDOK:
316  {
317  EndDialog(hwndDlg, IDOK);
318  }
319  break;
320  }
321  }
322  break;
323  case WM_NOTIFY:
324  {
325  NMHDR *header = (NMHDR *)lParam;
326  ULONG i;
327 
328  switch (header->code)
329  {
330  case GCN_GETDRAWINFO:
331  {
332  PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header;
333  PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo;
334 
335  drawInfo->Flags = PH_GRAPH_USE_GRID;
336  SysInfoParameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0);
337 
338  for (i = 0; i < EtGpuTotalNodeCount; i++)
339  {
340  if (header->hwndFrom == GraphHandle[i])
341  {
343  &GraphState[i],
344  getDrawInfo,
345  EtGpuNodesHistory[i].Count
346  );
347 
348  if (!GraphState[i].Valid)
349  {
350  PhCopyCircularBuffer_FLOAT(&EtGpuNodesHistory[i], GraphState[i].Data1, drawInfo->LineDataCount);
351  GraphState[i].Valid = TRUE;
352  }
353 
354  break;
355  }
356  }
357  }
358  break;
359  case GCN_GETTOOLTIPTEXT:
360  {
361  PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)header;
362 
363  if (getTooltipText->Index < getTooltipText->TotalCount)
364  {
365  for (i = 0; i < EtGpuTotalNodeCount; i++)
366  {
367  if (header->hwndFrom == GraphHandle[i])
368  {
369  if (GraphState[i].TooltipIndex != getTooltipText->Index)
370  {
371  FLOAT gpu;
372  ULONG adapterIndex;
373  PPH_STRING adapterDescription;
374 
375  gpu = PhGetItemCircularBuffer_FLOAT(&EtGpuNodesHistory[i], getTooltipText->Index);
376  adapterIndex = EtGetGpuAdapterIndexFromNodeIndex(i);
377 
378  if (adapterIndex != -1)
379  {
380  adapterDescription = EtGetGpuAdapterDescription(adapterIndex);
381 
382  if (adapterDescription && adapterDescription->Length == 0)
383  PhClearReference(&adapterDescription);
384 
385  if (!adapterDescription)
386  adapterDescription = PhFormatString(L"Adapter %lu", adapterIndex);
387  }
388  else
389  {
390  adapterDescription = PhCreateString(L"Unknown Adapter");
391  }
392 
393  PhMoveReference(&GraphState[i].TooltipText, PhFormatString(
394  L"Node %lu on %s\n%.2f%%\n%s",
395  i,
396  adapterDescription->Buffer,
397  gpu * 100,
398  ((PPH_STRING)PhAutoDereferenceObject(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer
399  ));
400  PhDereferenceObject(adapterDescription);
401  }
402 
403  getTooltipText->Text = GraphState[i].TooltipText->sr;
404 
405  break;
406  }
407  }
408  }
409  }
410  break;
411  }
412  }
413  break;
414  case UPDATE_MSG:
415  {
416  ULONG i;
417 
418  for (i = 0; i < EtGpuTotalNodeCount; i++)
419  {
420  GraphState[i].Valid = FALSE;
421  GraphState[i].TooltipIndex = -1;
422  Graph_MoveGrid(GraphHandle[i], 1);
423  Graph_Draw(GraphHandle[i]);
424  Graph_UpdateTooltip(GraphHandle[i]);
425  InvalidateRect(GraphHandle[i], NULL, FALSE);
426  }
427  }
428  break;
429  }
430 
431  return FALSE;
432 }