Panda3D
winGraphicsPipe.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file winGraphicsPipe.cxx
10  * @author drose
11  * @date 2002-12-20
12  */
13 
14 #include "winGraphicsPipe.h"
15 #include "config_windisplay.h"
17 #include "displayInformation.h"
18 #include "dtool_config.h"
19 #include "pbitops.h"
20 
21 #include <psapi.h>
22 #include <powrprof.h>
23 #include <intrin.h>
24 
25 TypeHandle WinGraphicsPipe::_type_handle;
26 
27 #ifndef MAXIMUM_PROCESSORS
28 #define MAXIMUM_PROCESSORS 32
29 #endif
30 
31 typedef enum _Process_DPI_Awareness {
32  Process_DPI_Unaware = 0,
33  Process_System_DPI_Aware = 1,
34  Process_Per_Monitor_DPI_Aware = 2
35 } Process_DPI_Awareness;
36 
37 typedef struct _PROCESSOR_POWER_INFORMATION {
38  ULONG Number;
39  ULONG MaxMhz;
40  ULONG CurrentMhz;
41  ULONG MhzLimit;
42  ULONG MaxIdleState;
43  ULONG CurrentIdleState;
44 } PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
45 
46 typedef BOOL (WINAPI *GetProcessMemoryInfoType) (HANDLE Process, PROCESS_MEMORY_COUNTERS *ppsmemCounters, DWORD cb);
47 typedef long (__stdcall *CallNtPowerInformationType) (POWER_INFORMATION_LEVEL information_level, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
48 
49 static int initialize = false;
50 static HMODULE psapi_dll = 0;
51 static GetProcessMemoryInfoType GetProcessMemoryInfoFunction = 0;
52 static CallNtPowerInformationType CallNtPowerInformationFunction = 0;
53 
54 void get_memory_information (DisplayInformation *display_information) {
55  if (initialize == false) {
56  psapi_dll = LoadLibrary("psapi.dll");
57  if (psapi_dll) {
58  GetProcessMemoryInfoFunction = (GetProcessMemoryInfoType) GetProcAddress(psapi_dll, "GetProcessMemoryInfo");
59  }
60 
61  initialize = true;
62  }
63 
64  MEMORYSTATUSEX memory_status;
65 
66  memory_status.dwLength = sizeof(MEMORYSTATUSEX);
67  if (GlobalMemoryStatusEx(&memory_status)) {
68  display_information->_physical_memory = memory_status.ullTotalPhys;
69  display_information->_available_physical_memory = memory_status.ullAvailPhys;
70  display_information->_page_file_size = memory_status.ullTotalPageFile;
71  display_information->_available_page_file_size = memory_status.ullAvailPageFile;
72  display_information->_process_virtual_memory = memory_status.ullTotalVirtual;
73  display_information->_available_process_virtual_memory = memory_status.ullAvailVirtual;
74  display_information->_memory_load = memory_status.dwMemoryLoad;
75  }
76 
77  if (GetProcessMemoryInfoFunction) {
78  HANDLE process;
79  DWORD process_id;
80  PROCESS_MEMORY_COUNTERS process_memory_counters;
81 
82  process_id = GetCurrentProcessId();
83  process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_id);
84  if (process) {
85  if (GetProcessMemoryInfoFunction (process, &process_memory_counters, sizeof(PROCESS_MEMORY_COUNTERS))) {
86  display_information->_page_fault_count = process_memory_counters.PageFaultCount;
87  display_information->_process_memory = process_memory_counters.WorkingSetSize;
88  display_information->_peak_process_memory = process_memory_counters.PeakWorkingSetSize;
89  display_information->_page_file_usage = process_memory_counters.PagefileUsage;
90  display_information->_peak_page_file_usage = process_memory_counters.PeakPagefileUsage;
91  }
92 
93  CloseHandle(process);
94  }
95  }
96 }
97 
98 int update_cpu_frequency_function(int processor_number, DisplayInformation *display_information) {
99  int update;
100 
101  update = false;
102  display_information->_maximum_cpu_frequency = 0;
103  display_information->_current_cpu_frequency = 0;
104 
105  if (CallNtPowerInformationFunction) {
106 
107  int i;
108  PVOID input_buffer;
109  PVOID output_buffer;
110  ULONG input_buffer_size;
111  ULONG output_buffer_size;
112  POWER_INFORMATION_LEVEL information_level;
113  PROCESSOR_POWER_INFORMATION *processor_power_information;
114  PROCESSOR_POWER_INFORMATION processor_power_information_array [MAXIMUM_PROCESSORS];
115 
116  memset(processor_power_information_array, 0, sizeof(PROCESSOR_POWER_INFORMATION) * MAXIMUM_PROCESSORS);
117 
118  processor_power_information = processor_power_information_array;
119  for (i = 0; i < MAXIMUM_PROCESSORS; i++) {
120  processor_power_information->Number = 0xFFFFFFFF;
121  processor_power_information++;
122  }
123 
124  information_level = ProcessorInformation;
125  input_buffer = nullptr;
126  output_buffer = processor_power_information_array;
127  input_buffer_size = 0;
128  output_buffer_size = sizeof(PROCESSOR_POWER_INFORMATION) * MAXIMUM_PROCESSORS;
129  if (CallNtPowerInformationFunction(information_level, input_buffer, input_buffer_size, output_buffer, output_buffer_size) == 0) {
130  processor_power_information = processor_power_information_array;
131  for (i = 0; i < MAXIMUM_PROCESSORS; i++) {
132  if (processor_power_information->Number == processor_number) {
133  uint64_t value;
134 
135  value = processor_power_information->MaxMhz;
136  display_information->_maximum_cpu_frequency = value * 1000000;
137 
138  value = processor_power_information->CurrentMhz;
139  display_information->_current_cpu_frequency = value * 1000000;
140  update = true;
141 
142  break;
143  }
144 
145  processor_power_information++;
146  }
147  }
148  }
149 
150  return update;
151 }
152 
153 void
154 count_number_of_cpus(DisplayInformation *display_information) {
155  int num_cpu_cores = 0;
156  int num_logical_cpus = 0;
157 
158  // Get a pointer to the GetLogicalProcessorInformation function.
159  typedef BOOL (WINAPI *LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
160  PDWORD);
161  LPFN_GLPI glpi;
162  glpi = (LPFN_GLPI)GetProcAddress(GetModuleHandle(TEXT("kernel32")),
163  "GetLogicalProcessorInformation");
164  if (glpi == nullptr) {
165  windisplay_cat.info()
166  << "GetLogicalProcessorInformation is not supported.\n";
167  return;
168  }
169 
170  // Allocate a buffer to hold the result of the
171  // GetLogicalProcessorInformation call.
172  PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = nullptr;
173  DWORD buffer_length = 0;
174  DWORD rc = glpi(buffer, &buffer_length);
175  while (!rc) {
176  if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
177  if (buffer != nullptr) {
178  PANDA_FREE_ARRAY(buffer);
179  }
180 
181  buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)PANDA_MALLOC_ARRAY(buffer_length);
182  nassertv(buffer != nullptr);
183  } else {
184  windisplay_cat.info()
185  << "GetLogicalProcessorInformation failed: " << GetLastError()
186  << "\n";
187  return;
188  }
189  rc = glpi(buffer, &buffer_length);
190  }
191 
192  // Now get the results.
193  PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = buffer;
194  PSYSTEM_LOGICAL_PROCESSOR_INFORMATION end = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)((char *)buffer + buffer_length);
195 
196  while (ptr < end) {
197  if (ptr->Relationship == RelationProcessorCore) {
198  num_cpu_cores++;
199 
200  // A hyperthreaded core supplies more than one logical processor.
201  num_logical_cpus += count_bits_in_word((uint64_t)(ptr->ProcessorMask));
202  }
203  ++ptr;
204  }
205 
206  PANDA_FREE_ARRAY(buffer);
207 
208  windisplay_cat.info()
209  << num_cpu_cores << " CPU cores, with "
210  << num_logical_cpus << " logical processors.\n";
211 
212  display_information->_num_cpu_cores = num_cpu_cores;
213  display_information->_num_logical_cpus = num_logical_cpus;
214 }
215 
216 
217 /**
218  *
219  */
220 WinGraphicsPipe::
221 WinGraphicsPipe() {
222  char string [512];
223 
224  _supported_types = OT_window | OT_fullscreen_window;
225 
226  HMODULE user32 = GetModuleHandleA("user32.dll");
227  if (user32 != nullptr) {
228  if (dpi_aware) {
229  typedef HRESULT (WINAPI *PFN_SETPROCESSDPIAWARENESS)(Process_DPI_Awareness);
230  PFN_SETPROCESSDPIAWARENESS pfnSetProcessDpiAwareness =
231  (PFN_SETPROCESSDPIAWARENESS)GetProcAddress(user32, "SetProcessDpiAwarenessInternal");
232 
233  if (pfnSetProcessDpiAwareness == nullptr) {
234  if (windisplay_cat.is_debug()) {
235  windisplay_cat.debug() << "Unable to find SetProcessDpiAwareness in user32.dll.\n";
236  }
237  } else {
238  if (windisplay_cat.is_debug()) {
239  windisplay_cat.debug() << "Calling SetProcessDpiAwareness().\n";
240  }
241  pfnSetProcessDpiAwareness(Process_Per_Monitor_DPI_Aware);
242 
243  HDC dc = GetDC(nullptr);
244  if (dc) {
245  int dpi = GetDeviceCaps(dc, LOGPIXELSX);
246  if (dpi > 0) {
247  PN_stdfloat zoom = (double)dpi / 96.0;
248  set_detected_display_zoom(zoom);
249 
250  if (windisplay_cat.is_debug()) {
251  windisplay_cat.debug()
252  << "Determined display zoom to be " << zoom
253  << " based on LOGPIXELSX " << dpi << "\n";
254  }
255  }
256  ReleaseDC(nullptr, dc);
257  }
258  }
259  }
260  }
261 
262 #ifdef HAVE_DX9
263  // Use D3D to get display info. This is disabled by default as it is slow.
264  if (request_dxdisplay_information) {
265  if (windisplay_cat.is_debug()) {
266  windisplay_cat.debug() << "Using Direct3D 9 to fetch display information.\n";
267  }
268  DisplaySearchParameters display_search_parameters_dx9;
269  int dx9_display_information (DisplaySearchParameters &display_search_parameters_dx9, DisplayInformation *display_information);
270  dx9_display_information(display_search_parameters_dx9, _display_information);
271  } else
272 #endif
273  {
274  // Use the Win32 API to query the available display modes.
275  if (windisplay_cat.is_debug()) {
276  windisplay_cat.debug() << "Using EnumDisplaySettings to fetch display information.\n";
277  }
278  pvector<DisplayMode> display_modes;
279  DEVMODE dm{};
280  dm.dmSize = sizeof(dm);
281  for (int i = 0; EnumDisplaySettings(nullptr, i, &dm) != 0; ++i) {
282  DisplayMode mode;
283  mode.width = dm.dmPelsWidth;
284  mode.height = dm.dmPelsHeight;
285  mode.bits_per_pixel = dm.dmBitsPerPel;
286  mode.refresh_rate = dm.dmDisplayFrequency;
287  mode.fullscreen_only = 0;
288  if (i == 0 || mode != display_modes.back()) {
289  display_modes.push_back(mode);
290  }
291  }
292 
293  // Copy this information to the DisplayInformation object.
294  _display_information->_total_display_modes = display_modes.size();
295  if (!display_modes.empty()) {
296  _display_information->_display_mode_array = new DisplayMode[display_modes.size()];
297  std::copy(display_modes.begin(), display_modes.end(),
298  _display_information->_display_mode_array);
299  }
300  }
301 
302  if (auto_cpu_data) {
303  lookup_cpu_data();
304  }
305 
306  OSVERSIONINFO version_info;
307 
308  version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
309  if (GetVersionEx(&version_info)) {
310  if (windisplay_cat.is_info()) {
311  sprintf(string, "OS version: %lu.%lu.%lu.%lu\n", version_info.dwMajorVersion, version_info.dwMinorVersion, version_info.dwPlatformId, version_info.dwBuildNumber);
312  windisplay_cat.info() << string;
313  windisplay_cat.info() << " " << version_info.szCSDVersion << "\n";
314  }
315 
316  _display_information->_os_version_major = version_info.dwMajorVersion;
317  _display_information->_os_version_minor = version_info.dwMinorVersion;
318  _display_information->_os_version_build = version_info.dwBuildNumber;
319  _display_information->_os_platform_id = version_info.dwPlatformId;
320  }
321  // Screen size
322  _display_width = GetSystemMetrics(SM_CXSCREEN);
323  _display_height = GetSystemMetrics(SM_CYSCREEN);
324 
325  HMODULE power_dll;
326 
327  power_dll = LoadLibrary("PowrProf.dll");
328  if (power_dll) {
329  CallNtPowerInformationFunction = (CallNtPowerInformationType) GetProcAddress(power_dll, "CallNtPowerInformation");
330  if (CallNtPowerInformationFunction) {
331 
332  _display_information->_update_cpu_frequency_function = update_cpu_frequency_function;
333  update_cpu_frequency_function(0, _display_information);
334 
335  sprintf(string, "max Mhz %I64d, current Mhz %I64d\n", _display_information->_maximum_cpu_frequency, _display_information->_current_cpu_frequency);
336 
337  windisplay_cat.info() << string;
338  }
339  }
340 }
341 
342 /**
343  * Looks up the detailed CPU information and stores it in
344  * _display_information, if supported by the OS. This may take a second or
345  * two.
346  */
348 lookup_cpu_data() {
349  char string [512];
350 
351  // set callback for memory function
352  _display_information->_get_memory_information_function = get_memory_information;
353 
354  // determine CPU frequency
355  uint64_t time;
356  uint64_t end_time;
357  LARGE_INTEGER counter;
358  LARGE_INTEGER end;
359  LARGE_INTEGER frequency;
360 
361  time = 0;
362  end_time = 0;
363  counter.QuadPart = 0;
364  end.QuadPart = 0;
365  frequency.QuadPart = 0;
366 
367  int priority;
368  HANDLE thread;
369 
370  windisplay_cat.info() << "begin QueryPerformanceFrequency\n";
371  thread = GetCurrentThread();
372  priority = GetThreadPriority (thread);
373  SetThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL);
374 
375  if (QueryPerformanceFrequency(&frequency)) {
376  if (frequency.QuadPart > 0) {
377  if (QueryPerformanceCounter (&counter)) {
378  time = __rdtsc();
379  end.QuadPart = counter.QuadPart + frequency.QuadPart;
380  while (QueryPerformanceCounter (&counter) && counter.QuadPart < end.QuadPart) {
381 
382  }
383  end_time = __rdtsc();
384 
385  _display_information->_cpu_frequency = end_time - time;
386  }
387  }
388  }
389 
390  SetThreadPriority(thread, priority);
391  sprintf(string, "QueryPerformanceFrequency: %I64d\n", frequency.QuadPart);
392  windisplay_cat.info() << string;
393  sprintf(string, "CPU frequency: %I64d\n", _display_information->_cpu_frequency);
394  windisplay_cat.info() << string;
395 
396  // Number of CPU's
397  count_number_of_cpus(_display_information);
398 }
399 
400 /**
401  *
402  */
403 WinGraphicsPipe::
404 ~WinGraphicsPipe() {
405 }
This class contains various display information.
Parameters used for searching display capabilities.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
virtual void lookup_cpu_data()
Looks up the detailed CPU information and stores it in _display_information, if supported by the OS.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int count_bits_in_word(unsigned short x)
Returns the number of 1 bits in the indicated word.
Definition: pbitops.I:18
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.