source: trunk/Packages/Graphics32/GR32_System.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 10.3 KB
Line 
1unit GR32_System;
2
3(* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1 or LGPL 2.1 with linking exception
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * Alternatively, the contents of this file may be used under the terms of the
17 * Free Pascal modified version of the GNU Lesser General Public License
18 * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
19 * of this license are applicable instead of those above.
20 * Please see the file LICENSE.txt for additional information concerning this
21 * license.
22 *
23 * The Original Code is Graphics32
24 *
25 * The Initial Developer of the Original Code is
26 * Alex A. Denisov
27 *
28 * Portions created by the Initial Developer are Copyright (C) 2000-2009
29 * the Initial Developer. All Rights Reserved.
30 *
31 * Contributor(s):
32 * Andre Beckedorf
33 * Michael Hansen <dyster_tid@hotmail.com>
34 * - CPU type & feature-set aware function binding
35 * - Runtime function template and extension binding system
36 *
37 * ***** END LICENSE BLOCK ***** *)
38
39interface
40
41{$I GR32.inc}
42
43uses
44{$IFDEF FPC}
45 LCLIntf, LCLType,
46 {$IFDEF Windows}
47 Windows,
48 {$ENDIF}
49 {$IFDEF UNIX}
50 Unix, BaseUnix,
51 {$ENDIF}
52{$ELSE}
53 Windows,
54{$ENDIF}
55 SysUtils;
56
57type
58 TPerfTimer = class
59 private
60{$IFDEF UNIX}
61 {$IFDEF FPC}
62 FStart: Int64;
63 {$ENDIF}
64{$ENDIF}
65{$IFDEF Windows}
66 FFrequency, FPerformanceCountStart, FPerformanceCountStop: Int64;
67{$ENDIF}
68 public
69 procedure Start;
70 function ReadNanoseconds: string;
71 function ReadMilliseconds: string;
72 function ReadSeconds: string;
73
74 function ReadValue: Int64;
75 end;
76
77{ Pseudo GetTickCount implementation for Linux - for compatibility
78 This works for basic time testing, however, it doesnt work like its
79 Windows counterpart, ie. it doesnt return the number of milliseconds since
80 system boot. Will definitely overflow. }
81function GetTickCount: Cardinal;
82
83{ Returns the number of processors configured by the operating system. }
84function GetProcessorCount: Cardinal;
85
86type
87 {$IFNDEF PUREPASCAL}
88 { TCPUInstructionSet, defines specific CPU technologies }
89 TCPUInstructionSet = (ciMMX, ciEMMX, ciSSE, ciSSE2, ci3DNow, ci3DNowExt);
90 {$ELSE}
91 TCPUInstructionSet = (ciDummy);
92 {$DEFINE NO_REQUIREMENTS}
93 {$ENDIF}
94
95 PCPUFeatures = ^TCPUFeatures;
96 TCPUFeatures = set of TCPUInstructionSet;
97
98{ General function that returns whether a particular instruction set is
99 supported for the current CPU or not }
100function HasInstructionSet(const InstructionSet: TCPUInstructionSet): Boolean;
101function CPUFeatures: TCPUFeatures;
102
103var
104 GlobalPerfTimer: TPerfTimer;
105
106implementation
107
108uses
109 Forms, Classes, TypInfo;
110
111var
112 CPUFeaturesInitialized : Boolean = False;
113 CPUFeaturesData: TCPUFeatures;
114
115{$IFDEF UNIX}
116{$IFDEF FPC}
117function GetTickCount: Cardinal;
118var
119 t : timeval;
120begin
121 fpgettimeofday(@t,nil);
122 // Build a 64 bit microsecond tick from the seconds and microsecond longints
123 Result := (Int64(t.tv_sec) * 1000000) + t.tv_usec;
124end;
125
126
127{ TPerfTimer }
128
129function TPerfTimer.ReadNanoseconds: string;
130begin
131 Result := IntToStr(ReadValue);
132end;
133
134function TPerfTimer.ReadMilliseconds: string;
135begin
136 Result := IntToStr(ReadValue div 1000);
137end;
138
139function TPerfTimer.ReadSeconds: string;
140begin
141 Result := IntToStr(ReadValue div 1000000);
142end;
143
144function TPerfTimer.ReadValue: Int64;
145begin
146 Result := GetTickCount - FStart;
147end;
148
149procedure TPerfTimer.Start;
150begin
151 FStart := GetTickCount;
152end;
153{$ENDIF}
154{$ENDIF}
155
156{$IFDEF Windows}
157function GetTickCount: Cardinal;
158begin
159 Result := Windows.GetTickCount;
160end;
161
162
163{ TPerfTimer }
164
165function TPerfTimer.ReadNanoseconds: string;
166begin
167 QueryPerformanceCounter(FPerformanceCountStop);
168 QueryPerformanceFrequency(FFrequency);
169 Assert(FFrequency > 0);
170
171 Result := IntToStr(Round(1000000 * (FPerformanceCountStop - FPerformanceCountStart) / FFrequency));
172end;
173
174function TPerfTimer.ReadMilliseconds: string;
175begin
176 QueryPerformanceCounter(FPerformanceCountStop);
177 QueryPerformanceFrequency(FFrequency);
178 Assert(FFrequency > 0);
179
180 Result := FloatToStrF(1000 * (FPerformanceCountStop - FPerformanceCountStart) / FFrequency, ffFixed, 15, 3);
181end;
182
183function TPerfTimer.ReadSeconds: String;
184begin
185 QueryPerformanceCounter(FPerformanceCountStop);
186 QueryPerformanceFrequency(FFrequency);
187 Result := FloatToStrF((FPerformanceCountStop - FPerformanceCountStart) / FFrequency, ffFixed, 15, 3);
188end;
189
190function TPerfTimer.ReadValue: Int64;
191begin
192 QueryPerformanceCounter(FPerformanceCountStop);
193 QueryPerformanceFrequency(FFrequency);
194 Assert(FFrequency > 0);
195
196 Result := Round(1000000 * (FPerformanceCountStop - FPerformanceCountStart) / FFrequency);
197end;
198
199procedure TPerfTimer.Start;
200begin
201 QueryPerformanceCounter(FPerformanceCountStart);
202end;
203{$ENDIF}
204
205{$IFDEF UNIX}
206{$IFDEF FPC}
207function GetProcessorCount: Cardinal;
208begin
209 Result := 1;
210end;
211{$ENDIF}
212{$ENDIF}
213{$IFDEF Windows}
214function GetProcessorCount: Cardinal;
215var
216 lpSysInfo: TSystemInfo;
217begin
218 GetSystemInfo(lpSysInfo);
219 Result := lpSysInfo.dwNumberOfProcessors;
220end;
221{$ENDIF}
222
223{$IFNDEF PUREPASCAL}
224const
225 CPUISChecks: array [TCPUInstructionSet] of Cardinal =
226 ($800000, $400000, $2000000, $4000000, $80000000, $40000000);
227 {ciMMX , ciEMMX, ciSSE , ciSSE2 , ci3DNow , ci3DNowExt}
228
229function CPUID_Available: Boolean;
230asm
231{$IFDEF TARGET_x86}
232 MOV EDX,False
233 PUSHFD
234 POP EAX
235 MOV ECX,EAX
236 XOR EAX,$00200000
237 PUSH EAX
238 POPFD
239 PUSHFD
240 POP EAX
241 XOR ECX,EAX
242 JZ @1
243 MOV EDX,True
244@1: PUSH EAX
245 POPFD
246 MOV EAX,EDX
247{$ENDIF}
248{$IFDEF TARGET_x64}
249 MOV EDX,False
250 PUSHFQ
251 POP RAX
252 MOV ECX,EAX
253 XOR EAX,$00200000
254 PUSH RAX
255 POPFQ
256 PUSHFQ
257 POP RAX
258 XOR ECX,EAX
259 JZ @1
260 MOV EDX,True
261@1: PUSH RAX
262 POPFQ
263 MOV EAX,EDX
264{$ENDIF}
265end;
266
267function CPU_Signature: Integer;
268asm
269{$IFDEF TARGET_x86}
270 PUSH EBX
271 MOV EAX,1
272 {$IFDEF FPC}
273 CPUID
274 {$ELSE}
275 DW $A20F // CPUID
276 {$ENDIF}
277 POP EBX
278{$ENDIF}
279{$IFDEF TARGET_x64}
280 PUSH RBX
281 MOV EAX,1
282 CPUID
283 POP RBX
284{$ENDIF}
285end;
286
287function CPU_Features: Integer;
288asm
289{$IFDEF TARGET_x86}
290 PUSH EBX
291 MOV EAX,1
292 {$IFDEF FPC}
293 CPUID
294 {$ELSE}
295 DW $A20F // CPUID
296 {$ENDIF}
297 POP EBX
298 MOV EAX,EDX
299{$ENDIF}
300{$IFDEF TARGET_x64}
301 PUSH RBX
302 MOV EAX,1
303 CPUID
304 POP RBX
305 MOV EAX,EDX
306{$ENDIF}
307end;
308
309function CPU_ExtensionsAvailable: Boolean;
310asm
311{$IFDEF TARGET_x86}
312 PUSH EBX
313 MOV @Result, True
314 MOV EAX, $80000000
315 {$IFDEF FPC}
316 CPUID
317 {$ELSE}
318 DW $A20F // CPUID
319 {$ENDIF}
320 CMP EAX, $80000000
321 JBE @NOEXTENSION
322 JMP @EXIT
323 @NOEXTENSION:
324 MOV @Result, False
325 @EXIT:
326 POP EBX
327{$ENDIF}
328{$IFDEF TARGET_x64}
329 PUSH RBX
330 MOV @Result, True
331 MOV EAX, $80000000
332 CPUID
333 CMP EAX, $80000000
334 JBE @NOEXTENSION
335 JMP @EXIT
336 @NOEXTENSION:
337 MOV @Result, False
338 @EXIT:
339 POP RBX
340{$ENDIF}
341end;
342
343function CPU_ExtFeatures: Integer;
344asm
345{$IFDEF TARGET_x86}
346 PUSH EBX
347 MOV EAX, $80000001
348 {$IFDEF FPC}
349 CPUID
350 {$ELSE}
351 DW $A20F // CPUID
352 {$ENDIF}
353 POP EBX
354 MOV EAX,EDX
355{$ENDIF}
356{$IFDEF TARGET_x64}
357 PUSH RBX
358 MOV EAX, $80000001
359 CPUID
360 POP RBX
361 MOV EAX,EDX
362{$ENDIF}
363end;
364
365function HasInstructionSet(const InstructionSet: TCPUInstructionSet): Boolean;
366// Must be implemented for each target CPU on which specific functions rely
367begin
368 Result := False;
369 if not CPUID_Available then Exit; // no CPUID available
370 if CPU_Signature shr 8 and $0F < 5 then Exit; // not a Pentium class
371
372 case InstructionSet of
373 ci3DNow, ci3DNowExt:
374 {$IFNDEF FPC}
375 if not CPU_ExtensionsAvailable or (CPU_ExtFeatures and CPUISChecks[InstructionSet] = 0) then
376 {$ENDIF}
377 Exit;
378 ciEMMX:
379 begin
380 // check for SSE, necessary for Intel CPUs because they don't implement the
381 // extended info
382 if (CPU_Features and CPUISChecks[ciSSE] = 0) and
383 (not CPU_ExtensionsAvailable or (CPU_ExtFeatures and CPUISChecks[ciEMMX] = 0)) then
384 Exit;
385 end;
386 else
387 if CPU_Features and CPUISChecks[InstructionSet] = 0 then
388 Exit; // return -> instruction set not supported
389 end;
390
391 Result := True;
392end;
393
394{$ELSE}
395
396function HasInstructionSet(const InstructionSet: TCPUInstructionSet): Boolean;
397begin
398 Result := False;
399end;
400{$ENDIF}
401
402procedure InitCPUFeaturesData;
403var
404 I: TCPUInstructionSet;
405begin
406 if CPUFeaturesInitialized then Exit;
407
408 CPUFeaturesData := [];
409 for I := Low(TCPUInstructionSet) to High(TCPUInstructionSet) do
410 if HasInstructionSet(I) then CPUFeaturesData := CPUFeaturesData + [I];
411
412 CPUFeaturesInitialized := True;
413end;
414
415function CPUFeatures: TCPUFeatures;
416begin
417 if not CPUFeaturesInitialized then
418 InitCPUFeaturesData;
419 Result := CPUFeaturesData;
420end;
421
422initialization
423 InitCPUFeaturesData;
424 GlobalPerfTimer := TPerfTimer.Create;
425
426finalization
427 GlobalPerfTimer.Free;
428
429end.
Note: See TracBrowser for help on using the repository browser.