GR32_System.pas 10.0 KB

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