GXS.CUDA.Compiler.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.CUDA.Compiler;
  5. (*
  6. Component allows to compile the CUDA-source (*.cu) file.
  7. in design- and runtime.
  8. To work requires the presence of CUDA Toolkit 3.X and MS Visual Studio C++.
  9. *)
  10. interface
  11. uses
  12. Winapi.Windows,
  13. Winapi.Messages,
  14. Winapi.ShellAPI,
  15. Winapi.TlHelp32,
  16. System.SysUtils,
  17. System.Classes,
  18. System.UITypes,
  19. FMX.Forms,
  20. FMX.Dialogs,
  21. GXS.ApplicationFileIO,
  22. Stage.Strings,
  23. CUDA.Parser;
  24. type
  25. TgxSCUDACompilerOutput = (codeUndefined, codePtx, codeCubin, codeGpu);
  26. (*
  27. compute_10 Basic features
  28. compute_11 + atomic memory operations on global memory
  29. compute_12 + atomic memory operations on shared memory
  30. + vote instructions
  31. compute_13 + double precision floating point support
  32. Compute_20 + FERMI support
  33. *)
  34. TgxSCUDAVirtArch = (compute_10, compute_11, compute_12, compute_13, compute_20);
  35. (*
  36. sm_10 ISA_1 Basic features
  37. sm_11 + atomic memory operations on global memory
  38. sm_12 + atomic memory operations on shared memory
  39. + vote instructions
  40. sm_13 + double precision floating point support
  41. sm_20 + FERMI support.
  42. sm_21 + Unknown
  43. *)
  44. TgxSCUDARealArch = (sm_10, sm_11, sm_12, sm_13, sm_20, sm_21);
  45. TgxSCUDARealArchs = set of TgxSCUDARealArch;
  46. TgxSCUDACompiler = class(TComponent)
  47. private
  48. FNVCCPath: string;
  49. FCppCompilerPath: string;
  50. FProduct: TStringList;
  51. FProjectModule: string;
  52. FSourceCodeFile: string;
  53. FConsoleContent: string;
  54. FOutputCodeType: TgxSCUDACompilerOutput;
  55. FVirtualArch: TgxSCUDAVirtArch;
  56. FRealArch: TgxSCUDARealArchs;
  57. FMaxRegisterCount: Integer;
  58. FModuleInfo: TCUDAModuleInfo;
  59. procedure SetMaxRegisterCount(Value: Integer);
  60. procedure SetOutputCodeType(const Value: TgxSCUDACompilerOutput);
  61. function StoreProjectModule: Boolean;
  62. procedure SetRealArch(AValue: TgxSCUDARealArchs);
  63. procedure SetNVCCPath(const AValue: string);
  64. procedure SetCppCompilerPath(const AValue: string);
  65. protected
  66. procedure Loaded; override;
  67. public
  68. constructor Create(AOwner: TComponent); override;
  69. destructor Destroy; override;
  70. procedure Assign(Source: TPersistent); override;
  71. procedure SetSourceCodeFile(const AFileName: string);
  72. function Compile: Boolean;
  73. // Product of compilation.
  74. property Product: TStringList read FProduct write FProduct;
  75. property ModuleInfo: TCUDAModuleInfo read FModuleInfo;
  76. property ConsoleContent: string read FConsoleContent;
  77. published
  78. // NVidia CUDA Compiler.
  79. property NVCCPath: string read FNVCCPath write SetNVCCPath;
  80. (* Microsoft Visual Studio Compiler.
  81. Pascal compiler is still not done. *)
  82. property CppCompilerPath: string read FCppCompilerPath
  83. write SetCppCompilerPath;
  84. // Full file name of source code file.
  85. property SourceCodeFile: string read FSourceCodeFile;
  86. (* Disign-time only property.
  87. Make choose of one of the Project module as CUDA kernel source *)
  88. property ProjectModule: string read FProjectModule write FProjectModule
  89. stored StoreProjectModule;
  90. (* Output code type for module kernel
  91. - Ptx - Parallel Thread Execution
  92. - Cubin - CUDA Binary *)
  93. property OutputCodeType: TgxSCUDACompilerOutput read FOutputCodeType
  94. write setOutputCodeType default codePtx;
  95. (* In the CUDA naming scheme,
  96. GPUs are named sm_xy,
  97. where x denotes the GPU generation number,
  98. and y the version in that generation. *)
  99. property RealArchitecture: TgxSCUDARealArchs read FRealArch
  100. write SetRealArch default [sm_13];
  101. // Virtual architecture.
  102. property VirtualArchitecture: TgxSCUDAVirtArch read FVirtualArch
  103. write FVirtualArch default compute_13;
  104. // Maximum registers that kernel can use.
  105. property MaxRegisterCount: Integer read FMaxRegisterCount
  106. write SetMaxRegisterCount default 32;
  107. end;
  108. TFindCuFileFunc = function(var AModuleName: string): Boolean;
  109. var
  110. vFindCuFileFunc: TFindCuFileFunc;
  111. //=========================================
  112. implementation
  113. //=========================================
  114. // ------------------
  115. // ------------------ TgxSCUDACompiler ------------------
  116. // ------------------
  117. constructor TgxSCUDACompiler.Create(AOwner: TComponent);
  118. var
  119. path: string;
  120. begin
  121. inherited Create(AOwner);
  122. FOutputCodeType := codePtx;
  123. FVirtualArch := compute_13;
  124. FRealArch := [sm_13];
  125. FMaxRegisterCount := 32;
  126. FNVCCPath := '';
  127. path := GetEnvironmentVariable('CUDA_BIN_PATH');
  128. if Length(path) > 0 then
  129. begin
  130. path := IncludeTrailingPathDelimiter(path);
  131. if FileExists(path + 'nvcc.exe') then
  132. FNVCCPath := path;
  133. end;
  134. path := 'C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\';
  135. if FileExists(path + 'cl.exe') then
  136. FCppCompilerPath := path
  137. else
  138. begin
  139. path := 'C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\';
  140. if FileExists(path + 'cl.exe') then
  141. FCppCompilerPath := path
  142. else
  143. begin
  144. path := 'C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\';
  145. if FileExists(path + 'cl.exe') then
  146. FCppCompilerPath := path
  147. else
  148. begin
  149. path := 'C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\';
  150. if FileExists(path + 'cl.exe') then
  151. FCppCompilerPath := path
  152. else
  153. begin
  154. path := 'C:\Program Files\Microsoft Visual Studio 8\VC\bin\';
  155. if FileExists(path + 'cl.exe') then
  156. FCppCompilerPath := path
  157. else
  158. begin
  159. path := 'C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin\';
  160. if FileExists(path + 'cl.exe') then
  161. FCppCompilerPath := path
  162. else
  163. FCppCompilerPath := '';
  164. end;
  165. end;
  166. end;
  167. end;
  168. end;
  169. FProjectModule := 'none';
  170. FModuleInfo := TCUDAModuleInfo.Create;
  171. end;
  172. destructor TgxSCUDACompiler.Destroy;
  173. begin
  174. FModuleInfo.Destroy;
  175. inherited;
  176. end;
  177. procedure TgxSCUDACompiler.Loaded;
  178. var
  179. LStr: string;
  180. begin
  181. inherited;
  182. if (FProjectModule <> 'none') and Assigned(vFindCuFileFunc) then
  183. begin
  184. LStr := FProjectModule;
  185. if vFindCuFileFunc(LStr) then
  186. FSourceCodeFile := LStr
  187. else
  188. FSourceCodeFile := '';
  189. end;
  190. end;
  191. procedure TgxSCUDACompiler.Assign(Source: TPersistent);
  192. var
  193. compiler: TgxSCUDACompiler;
  194. begin
  195. if Source is TgxSCUDACompiler then
  196. begin
  197. compiler := TgxSCUDACompiler(Source);
  198. FSourceCodeFile := compiler.FSourceCodeFile;
  199. FOutputCodeType := compiler.FOutputCodeType;
  200. FVirtualArch := compiler.FVirtualArch;
  201. end;
  202. inherited Assign(Source);
  203. end;
  204. function TgxSCUDACompiler.Compile: Boolean;
  205. const
  206. ReadBufferSize = 1048576; // 1 MB Buffer
  207. cSM: array[TgxSCUDARealArch] of string =
  208. ('sm_10', 'sm_11', 'sm_12', 'sm_13', 'sm_20', 'sm_21');
  209. var
  210. tepmPath, tempFile, tempFileExt: string;
  211. commands, nvcc, pathfile, msg: string;
  212. rArch: TgxSCUDARealArch;
  213. CodeSource: TStringList;
  214. Security: TSecurityAttributes;
  215. ReadPipe, WritePipe: THandle;
  216. start: TStartUpInfo;
  217. ProcessInfo: TProcessInformation;
  218. Buffer: PAnsiChar;
  219. TotalBytesRead, BytesRead: DWORD;
  220. Apprunning, n, BytesLeftThisMessage, TotalBytesAvail: Integer;
  221. begin
  222. if not FileExists(FSourceCodeFile) then
  223. begin
  224. MessageDlg(strSourceFileNotFound, TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
  225. Exit(false);
  226. end;
  227. CodeSource := TStringList.Create;
  228. CodeSource.LoadFromFile(FSourceCodeFile);
  229. Result := false;
  230. FConsoleContent := '';
  231. if FileExists(FNVCCPath + 'nvcc.exe') and
  232. FileExists(FCppCompilerPath + 'cl.exe') and Assigned(FProduct) then
  233. begin
  234. tepmPath := GetEnvironmentVariable('TEMP');
  235. tepmPath := IncludeTrailingPathDelimiter(tepmPath);
  236. tempFile := tepmPath + 'temp';
  237. CodeSource.SaveToFile(tempFile + '.cu');
  238. commands := '"' + tempFile + '.cu" ';
  239. commands := commands + '-arch ';
  240. case FVirtualArch of
  241. compute_10:
  242. commands := commands + 'compute_10 ';
  243. compute_11:
  244. commands := commands + 'compute_11 ';
  245. compute_12:
  246. commands := commands + 'compute_12 ';
  247. compute_13:
  248. commands := commands + 'compute_13 ';
  249. compute_20:
  250. commands := commands + 'compute_20 ';
  251. end;
  252. commands := commands + '-code ';
  253. for rArch in FRealArch do
  254. commands := commands + cSM[rArch] + ', ';
  255. commands[Length(commands)-1] := ' ';
  256. commands := commands + '-ccbin ';
  257. pathfile := Copy(FCppCompilerPath, 1, Length(FCppCompilerPath) - 1);
  258. commands := commands + '"' + pathfile + '" ';
  259. commands := commands + '-Xcompiler "/EHsc /W3 /nologo /O2 /Zi /MT " ';
  260. commands := commands + '-maxrregcount=' + IntToStr(FMaxRegisterCount) + ' ';
  261. commands := commands + '-m32 ';
  262. case FOutputCodeType of
  263. codePtx:
  264. begin
  265. commands := commands + '--ptx ';
  266. tempFileExt := 'ptx';
  267. end;
  268. codeCubin:
  269. begin
  270. commands := commands + '--cubin ';
  271. tempFileExt := 'cubin';
  272. end;
  273. codeGpu:
  274. begin
  275. commands := commands + '--gpu ';
  276. tempFileExt := 'gpu';
  277. end;
  278. end;
  279. commands := commands + '-o "' + tempFile + '.' + tempFileExt + '" ';
  280. commands := commands + #00;
  281. nvcc := FNVCCPath + 'nvcc.exe ';
  282. with Security do
  283. begin
  284. nlength := SizeOf(TSecurityAttributes);
  285. binherithandle := true;
  286. lpsecuritydescriptor := nil;
  287. end;
  288. if CreatePipe(ReadPipe, WritePipe, @Security, 0) then
  289. begin
  290. // Redirect In- and Output through STARTUPINFO structure
  291. Buffer := AllocMem(ReadBufferSize + 1);
  292. FillChar(start, SizeOf(start), #0);
  293. start.cb := SizeOf(start);
  294. start.hStdOutput := WritePipe;
  295. start.hStdInput := ReadPipe;
  296. start.hStdError := WritePipe;
  297. start.dwFlags := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
  298. start.wShowWindow := SW_HIDE;
  299. // Create a Console Child Process with redirected input and output
  300. if CreateProcess(nil, PChar(nvcc+commands), @Security, @Security, true,
  301. CREATE_NO_WINDOW or NORMAL_PRIORITY_CLASS, nil, nil, start,
  302. ProcessInfo) then
  303. begin
  304. n := 0;
  305. TotalBytesRead := 0;
  306. repeat
  307. // Increase counter to prevent an endless loop if the process is dead
  308. Inc(n, 1);
  309. // wait for end of child process
  310. Apprunning := WaitForSingleObject(ProcessInfo.hProcess, 100);
  311. Application.ProcessMessages;
  312. // it is important to read from time to time the output information
  313. // so that the pipe is not blocked by an overflow. New information
  314. // can be written from the console app to the pipe only if there is
  315. // enough buffer space.
  316. if not PeekNamedPipe(ReadPipe, @Buffer[TotalBytesRead],
  317. ReadBufferSize, @BytesRead, @TotalBytesAvail,
  318. @BytesLeftThisMessage) then
  319. break
  320. else if BytesRead > 0 then
  321. ReadFile(ReadPipe, Buffer[TotalBytesRead], BytesRead,
  322. BytesRead, nil);
  323. TotalBytesRead := TotalBytesRead + BytesRead;
  324. until (Apprunning <> WAIT_TIMEOUT) or (n > 150);
  325. Buffer[TotalBytesRead] := #00;
  326. OemToCharA(Buffer, Buffer);
  327. end
  328. else
  329. MessageDlg(strFailRunNVCC, TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
  330. pathfile := tempFile + '.' + tempFileExt;
  331. if FileExists(pathfile) then
  332. begin
  333. FProduct.LoadFromFile(pathfile);
  334. FModuleInfo.ParseModule(CodeSource, FProduct);
  335. if csDesigning in ComponentState then
  336. FProduct.OnChange(Self);
  337. DeleteFile(pathfile);
  338. Result := true;
  339. FConsoleContent := string(StrPas(Buffer));
  340. msg := Format(strSuccessCompilation, [FConsoleContent]);
  341. if csDesigning in ComponentState then
  342. MessageDlg(msg, TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0)
  343. else
  344. MessageDlg(msg, TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
  345. end
  346. else
  347. begin
  348. msg := Format(strFailCompilation, [StrPas(Buffer)]);
  349. MessageDlg(msg, TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
  350. end;
  351. FreeMem(Buffer);
  352. CloseHandle(ProcessInfo.hProcess);
  353. CloseHandle(ProcessInfo.hThread);
  354. CloseHandle(ReadPipe);
  355. CloseHandle(WritePipe);
  356. end
  357. else
  358. MessageDlg(strFailCreatePipe, TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
  359. pathfile := tempFile + '.cu';
  360. DeleteFile(pathfile);
  361. end;
  362. CodeSource.Free;
  363. end;
  364. procedure TgxSCUDACompiler.SetCppCompilerPath(const AValue: string);
  365. begin
  366. if FileExists(AValue + 'cl.exe') then
  367. FCppCompilerPath := AValue;
  368. end;
  369. procedure TgxSCUDACompiler.setMaxRegisterCount(Value: Integer);
  370. begin
  371. if Value <> FMaxRegisterCount then
  372. begin
  373. Value := 4 * (Value div 4);
  374. if Value < 4 then
  375. Value := 4;
  376. if Value > 128 then
  377. Value := 128;
  378. FMaxRegisterCount := Value;
  379. end;
  380. end;
  381. procedure TgxSCUDACompiler.SetNVCCPath(const AValue: string);
  382. begin
  383. if FileExists(AValue + 'nvcc.exe') then
  384. FNVCCPath := AValue;
  385. end;
  386. procedure TgxSCUDACompiler.setOutputCodeType(const Value
  387. : TgxSCUDACompilerOutput);
  388. begin
  389. if Value = codeUndefined then
  390. exit;
  391. FOutputCodeType := Value;
  392. end;
  393. procedure TgxSCUDACompiler.SetRealArch(AValue: TgxSCUDARealArchs);
  394. begin
  395. if AValue = [] then
  396. AValue := [sm_10];
  397. FRealArch := AValue;
  398. end;
  399. procedure TgxSCUDACompiler.SetSourceCodeFile(const AFileName: string);
  400. begin
  401. if FileStreamExists(AFileName) then
  402. FSourceCodeFile := AFileName;
  403. end;
  404. function TgxSCUDACompiler.StoreProjectModule: Boolean;
  405. begin
  406. Result := FProjectModule <> 'none';
  407. end;
  408. end.