CUDA.Compiler.pas 13 KB

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