GR32_System.pas 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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. SysUtils;
  41. type
  42. TPerfTimer = class
  43. private
  44. FStart: Int64;
  45. public
  46. procedure Start;
  47. function ReadNanoseconds: string;
  48. function ReadMilliseconds: string;
  49. function ReadSeconds: string;
  50. function ReadValue: Int64;
  51. end;
  52. { Pseudo GetTickCount implementation for Linux - for compatibility
  53. This works for basic time testing, however, it doesnt work like its
  54. Windows counterpart, ie. it doesnt return the number of milliseconds since
  55. system boot. Will definitely overflow. }
  56. function GetTickCount: Cardinal;
  57. { Returns the number of processors configured by the operating system. }
  58. function GetProcessorCount: Cardinal;
  59. type
  60. {$IFNDEF PUREPASCAL}
  61. { TCPUInstructionSet, defines specific CPU technologies }
  62. TCPUInstructionSet = (ciMMX, ciEMMX, ciSSE, ciSSE2, ci3DNow, ci3DNowExt);
  63. {$ELSE}
  64. TCPUInstructionSet = (ciDummy);
  65. {$DEFINE NO_REQUIREMENTS}
  66. {$ENDIF}
  67. PCPUFeatures = ^TCPUFeatures;
  68. TCPUFeatures = set of TCPUInstructionSet;
  69. { General function that returns whether a particular instruction set is
  70. supported for the current CPU or not }
  71. function HasInstructionSet(const InstructionSet: TCPUInstructionSet): Boolean;
  72. function CPUFeatures: TCPUFeatures;
  73. var
  74. GlobalPerfTimer: TPerfTimer;
  75. implementation
  76. uses
  77. {$IF defined(MSWINDOWS)}
  78. Winapi.Windows
  79. {$ELSEIF defined(MACOS)}
  80. Macapi.Mach
  81. {$ELSEIF defined(POSIX)}
  82. Posix.Time
  83. {$ENDIF}
  84. ,
  85. Classes, TypInfo;
  86. var
  87. CPUFeaturesInitialized : Boolean = False;
  88. CPUFeaturesData: TCPUFeatures;
  89. function GetTickCount: Cardinal;
  90. begin
  91. Result := TThread.GetTickCount;
  92. end;
  93. { TPerfTimer }
  94. function TPerfTimer.ReadNanoseconds: string;
  95. begin
  96. Result := IntToStr(ReadValue);
  97. end;
  98. function TPerfTimer.ReadMilliseconds: string;
  99. begin
  100. Result := IntToStr(ReadValue div 1000);
  101. end;
  102. function TPerfTimer.ReadSeconds: string;
  103. begin
  104. Result := IntToStr(ReadValue div 1000000);
  105. end;
  106. function TPerfTimer.ReadValue: Int64;
  107. begin
  108. Result := GetTickCount - FStart;
  109. end;
  110. procedure TPerfTimer.Start;
  111. begin
  112. FStart := GetTickCount;
  113. end;
  114. function GetProcessorCount: Cardinal;
  115. begin
  116. Result := 1;
  117. end;
  118. {$IFNDEF PUREPASCAL}
  119. const
  120. CPUISChecks: array [TCPUInstructionSet] of Cardinal =
  121. ($800000, $400000, $2000000, $4000000, $80000000, $40000000);
  122. {ciMMX , ciEMMX, ciSSE , ciSSE2 , ci3DNow , ci3DNowExt}
  123. function CPUID_Available: Boolean;
  124. asm
  125. {$IFDEF TARGET_x86}
  126. MOV EDX,False
  127. PUSHFD
  128. POP EAX
  129. MOV ECX,EAX
  130. XOR EAX,$00200000
  131. PUSH EAX
  132. POPFD
  133. PUSHFD
  134. POP EAX
  135. XOR ECX,EAX
  136. JZ @1
  137. MOV EDX,True
  138. @1: PUSH EAX
  139. POPFD
  140. MOV EAX,EDX
  141. {$ENDIF}
  142. {$IFDEF TARGET_x64}
  143. MOV EDX,False
  144. PUSHFQ
  145. POP RAX
  146. MOV ECX,EAX
  147. XOR EAX,$00200000
  148. PUSH RAX
  149. POPFQ
  150. PUSHFQ
  151. POP RAX
  152. XOR ECX,EAX
  153. JZ @1
  154. MOV EDX,True
  155. @1: PUSH RAX
  156. POPFQ
  157. MOV EAX,EDX
  158. {$ENDIF}
  159. end;
  160. function CPU_Signature: Integer;
  161. asm
  162. {$IFDEF TARGET_x86}
  163. PUSH EBX
  164. MOV EAX,1
  165. {$IFDEF FPC}
  166. CPUID
  167. {$ELSE}
  168. DW $A20F // CPUID
  169. {$ENDIF}
  170. POP EBX
  171. {$ENDIF}
  172. {$IFDEF TARGET_x64}
  173. PUSH RBX
  174. MOV EAX,1
  175. CPUID
  176. POP RBX
  177. {$ENDIF}
  178. end;
  179. function CPU_Features: Integer;
  180. asm
  181. {$IFDEF TARGET_x86}
  182. PUSH EBX
  183. MOV EAX,1
  184. {$IFDEF FPC}
  185. CPUID
  186. {$ELSE}
  187. DW $A20F // CPUID
  188. {$ENDIF}
  189. POP EBX
  190. MOV EAX,EDX
  191. {$ENDIF}
  192. {$IFDEF TARGET_x64}
  193. PUSH RBX
  194. MOV EAX,1
  195. CPUID
  196. POP RBX
  197. MOV EAX,EDX
  198. {$ENDIF}
  199. end;
  200. function CPU_ExtensionsAvailable: Boolean;
  201. asm
  202. {$IFDEF TARGET_x86}
  203. PUSH EBX
  204. MOV @Result, True
  205. MOV EAX, $80000000
  206. {$IFDEF FPC}
  207. CPUID
  208. {$ELSE}
  209. DW $A20F // CPUID
  210. {$ENDIF}
  211. CMP EAX, $80000000
  212. JBE @NOEXTENSION
  213. JMP @EXIT
  214. @NOEXTENSION:
  215. MOV @Result, False
  216. @EXIT:
  217. POP EBX
  218. {$ENDIF}
  219. {$IFDEF TARGET_x64}
  220. PUSH RBX
  221. MOV @Result, True
  222. MOV EAX, $80000000
  223. CPUID
  224. CMP EAX, $80000000
  225. JBE @NOEXTENSION
  226. JMP @EXIT
  227. @NOEXTENSION:
  228. MOV @Result, False
  229. @EXIT:
  230. POP RBX
  231. {$ENDIF}
  232. end;
  233. function CPU_ExtFeatures: Integer;
  234. asm
  235. {$IFDEF TARGET_x86}
  236. PUSH EBX
  237. MOV EAX, $80000001
  238. {$IFDEF FPC}
  239. CPUID
  240. {$ELSE}
  241. DW $A20F // CPUID
  242. {$ENDIF}
  243. POP EBX
  244. MOV EAX,EDX
  245. {$ENDIF}
  246. {$IFDEF TARGET_x64}
  247. PUSH RBX
  248. MOV EAX, $80000001
  249. CPUID
  250. POP RBX
  251. MOV EAX,EDX
  252. {$ENDIF}
  253. end;
  254. function HasInstructionSet(const InstructionSet: TCPUInstructionSet): Boolean;
  255. // Must be implemented for each target CPU on which specific functions rely
  256. begin
  257. Result := False;
  258. if not CPUID_Available then Exit; // no CPUID available
  259. if CPU_Signature shr 8 and $0F < 5 then Exit; // not a Pentium class
  260. case InstructionSet of
  261. ci3DNow, ci3DNowExt:
  262. {$IFNDEF FPC}
  263. if not CPU_ExtensionsAvailable or (CPU_ExtFeatures and CPUISChecks[InstructionSet] = 0) then
  264. {$ENDIF}
  265. Exit;
  266. ciEMMX:
  267. begin
  268. // check for SSE, necessary for Intel CPUs because they don't implement the
  269. // extended info
  270. if (CPU_Features and CPUISChecks[ciSSE] = 0) and
  271. (not CPU_ExtensionsAvailable or (CPU_ExtFeatures and CPUISChecks[ciEMMX] = 0)) then
  272. Exit;
  273. end;
  274. else
  275. if CPU_Features and CPUISChecks[InstructionSet] = 0 then
  276. Exit; // return -> instruction set not supported
  277. end;
  278. Result := True;
  279. end;
  280. {$ELSE}
  281. function HasInstructionSet(const InstructionSet: TCPUInstructionSet): Boolean;
  282. begin
  283. Result := False;
  284. end;
  285. {$ENDIF}
  286. procedure InitCPUFeaturesData;
  287. var
  288. I: TCPUInstructionSet;
  289. begin
  290. if CPUFeaturesInitialized then Exit;
  291. CPUFeaturesData := [];
  292. for I := Low(TCPUInstructionSet) to High(TCPUInstructionSet) do
  293. if HasInstructionSet(I) then CPUFeaturesData := CPUFeaturesData + [I];
  294. CPUFeaturesInitialized := True;
  295. end;
  296. function CPUFeatures: TCPUFeatures;
  297. begin
  298. if not CPUFeaturesInitialized then
  299. InitCPUFeaturesData;
  300. Result := CPUFeaturesData;
  301. end;
  302. initialization
  303. InitCPUFeaturesData;
  304. GlobalPerfTimer := TPerfTimer.Create;
  305. finalization
  306. GlobalPerfTimer.Free;
  307. end.