GLS.CUDACompiler.pas 14 KB

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