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  }
244  }
245 
246 #ifdef HAVE_DX9
247  // Use D3D to get display info. This is disabled by default as it is slow.
248  if (request_dxdisplay_information) {
249  if (windisplay_cat.is_debug()) {
250  windisplay_cat.debug() << "Using Direct3D 9 to fetch display information.\n";
251  }
252  DisplaySearchParameters display_search_parameters_dx9;
253  int dx9_display_information (DisplaySearchParameters &display_search_parameters_dx9, DisplayInformation *display_information);
254  dx9_display_information(display_search_parameters_dx9, _display_information);
255  } else
256 #endif
257  {
258  // Use the Win32 API to query the available display modes.
259  if (windisplay_cat.is_debug()) {
260  windisplay_cat.debug() << "Using EnumDisplaySettings to fetch display information.\n";
261  }
262  pvector<DisplayMode> display_modes;
263  DEVMODE dm{};
264  dm.dmSize = sizeof(dm);
265  for (int i = 0; EnumDisplaySettings(nullptr, i, &dm) != 0; ++i) {
266  DisplayMode mode;
267  mode.width = dm.dmPelsWidth;
268  mode.height = dm.dmPelsHeight;
269  mode.bits_per_pixel = dm.dmBitsPerPel;
270  mode.refresh_rate = dm.dmDisplayFrequency;
271  mode.fullscreen_only = 0;
272  if (i == 0 || mode != display_modes.back()) {
273  display_modes.push_back(mode);
274  }
275  }
276 
277  // Copy this information to the DisplayInformation object.
278  _display_information->_total_display_modes = display_modes.size();
279  if (!display_modes.empty()) {
280  _display_information->_display_mode_array = new DisplayMode[display_modes.size()];
281  std::copy(display_modes.begin(), display_modes.end(),
282  _display_information->_display_mode_array);
283  }
284  }
285 
286  if (auto_cpu_data) {
287  lookup_cpu_data();
288  }
289 
290  OSVERSIONINFO version_info;
291 
292  version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
293  if (GetVersionEx(&version_info)) {
294  if (windisplay_cat.is_info()) {
295  sprintf(string, "OS version: %lu.%lu.%lu.%lu\n", version_info.dwMajorVersion, version_info.dwMinorVersion, version_info.dwPlatformId, version_info.dwBuildNumber);
296  windisplay_cat.info() << string;
297  windisplay_cat.info() << " " << version_info.szCSDVersion << "\n";
298  }
299 
300  _display_information->_os_version_major = version_info.dwMajorVersion;
301  _display_information->_os_version_minor = version_info.dwMinorVersion;
302  _display_information->_os_version_build = version_info.dwBuildNumber;
303  _display_information->_os_platform_id = version_info.dwPlatformId;
304  }
305  // Screen size
306  _display_width = GetSystemMetrics(SM_CXSCREEN);
307  _display_height = GetSystemMetrics(SM_CYSCREEN);
308 
309  HMODULE power_dll;
310 
311  power_dll = LoadLibrary("PowrProf.dll");
312  if (power_dll) {
313  CallNtPowerInformationFunction = (CallNtPowerInformationType) GetProcAddress(power_dll, "CallNtPowerInformation");
314  if (CallNtPowerInformationFunction) {
315 
316  _display_information->_update_cpu_frequency_function = update_cpu_frequency_function;
317  update_cpu_frequency_function(0, _display_information);
318 
319  sprintf(string, "max Mhz %I64d, current Mhz %I64d\n", _display_information->_maximum_cpu_frequency, _display_information->_current_cpu_frequency);
320 
321  windisplay_cat.info() << string;
322  }
323  }
324 }
325 
326 /**
327  * Looks up the detailed CPU information and stores it in
328  * _display_information, if supported by the OS. This may take a second or
329  * two.
330  */
333  char string [512];
334 
335  // set callback for memory function
336  _display_information->_get_memory_information_function = get_memory_information;
337 
338  // determine CPU frequency
339  uint64_t time;
340  uint64_t end_time;
341  LARGE_INTEGER counter;
342  LARGE_INTEGER end;
343  LARGE_INTEGER frequency;
344 
345  time = 0;
346  end_time = 0;
347  counter.QuadPart = 0;
348  end.QuadPart = 0;
349  frequency.QuadPart = 0;
350 
351  int priority;
352  HANDLE thread;
353 
354  windisplay_cat.info() << "begin QueryPerformanceFrequency\n";
355  thread = GetCurrentThread();
356  priority = GetThreadPriority (thread);
357  SetThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL);
358 
359  if (QueryPerformanceFrequency(&frequency)) {
360  if (frequency.QuadPart > 0) {
361  if (QueryPerformanceCounter (&counter)) {
362  time = __rdtsc();
363  end.QuadPart = counter.QuadPart + frequency.QuadPart;
364  while (QueryPerformanceCounter (&counter) && counter.QuadPart < end.QuadPart) {
365 
366  }
367  end_time = __rdtsc();
368 
369  _display_information->_cpu_frequency = end_time - time;
370  }
371  }
372  }
373 
374  SetThreadPriority(thread, priority);
375  sprintf(string, "QueryPerformanceFrequency: %I64d\n", frequency.QuadPart);
376  windisplay_cat.info() << string;
377  sprintf(string, "CPU frequency: %I64d\n", _display_information->_cpu_frequency);
378  windisplay_cat.info() << string;
379 
380  // Number of CPU's
381  count_number_of_cpus(_display_information);
382 }
383 
384 /**
385  *
386  */
387 WinGraphicsPipe::
388 ~WinGraphicsPipe() {
389 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class contains various display information.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
Parameters used for searching display capabilities.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
int count_bits_in_word(uint16_t x)
Returns the number of 1 bits in the indicated word.
Definition: pbitops.I:18
virtual void lookup_cpu_data()
Looks up the detailed CPU information and stores it in _display_information, if supported by the OS.