GXS.CUDA.Context.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.CUDA.Context;
  5. interface
  6. uses
  7. System.Classes,
  8. System.SysUtils,
  9. FMX.Dialogs,
  10. CUDA.Import,
  11. CUDA.RunTime,
  12. Stage.Strings,
  13. Stage.Generics,
  14. GXS.BaseClasses,
  15. GXS.Context;
  16. type
  17. TCUDADimensions = class(TgxUpdateAbleObject)
  18. private
  19. FXYZ: TDim3;
  20. FMaxXYZ: TDim3;
  21. FReadOnly: Boolean;
  22. function GetDimComponent(index: Integer): Integer;
  23. procedure SetDimComponent(index: Integer; Value: Integer);
  24. function GetMaxDimComponent(index: Integer): Integer;
  25. procedure SetMaxDimComponent(index: Integer; Value: Integer);
  26. public
  27. constructor Create(AOwner: TPersistent); override;
  28. procedure Assign(Source: TPersistent); override;
  29. property MaxSizeX: Integer index 0 read GetMaxDimComponent
  30. write SetMaxDimComponent;
  31. property MaxSizeY: Integer index 1 read GetMaxDimComponent
  32. write SetMaxDimComponent;
  33. property MaxSizeZ: Integer index 2 read GetMaxDimComponent
  34. write SetMaxDimComponent;
  35. property ReadOnlyValue: Boolean read FReadOnly write FReadOnly;
  36. published
  37. property SizeX: Integer index 0 read GetDimComponent write SetDimComponent
  38. default 1;
  39. property SizeY: Integer index 1 read GetDimComponent write SetDimComponent
  40. default 1;
  41. property SizeZ: Integer index 2 read GetDimComponent write SetDimComponent
  42. default 1;
  43. end;
  44. TCUDAContext = class;
  45. TOnOpenGLInteropInit = procedure(out Context: TgxContext) of object;
  46. TCUDADevice = class(TPersistent)
  47. private
  48. fID: Integer;
  49. fHandle: TCUdevice;
  50. fGFlops: Integer;
  51. fDeviceProperties: TCudaDeviceProp;
  52. FSuitable: Boolean;
  53. FUsed: Boolean;
  54. fMaxThreadsDim: TCUDADimensions;
  55. fMaxGridSize: TCUDADimensions;
  56. protected
  57. function GetName: string;
  58. public
  59. constructor Create; reintroduce;
  60. destructor Destroy; override;
  61. procedure Assign(Source: TPersistent); override;
  62. (* Returns in bytes the total amount of memory
  63. available on the device dev in bytes. *)
  64. function TotalMemory: Cardinal;
  65. published
  66. property Name: string read GetName;
  67. property TotalGlobalMem: NativeUInt read fDeviceProperties.TotalGlobalMem;
  68. property SharedMemPerBlock: NativeUInt read fDeviceProperties.SharedMemPerBlock;
  69. property RegsPerBlock: Integer read fDeviceProperties.RegsPerBlock;
  70. property WarpSize: Integer read fDeviceProperties.WarpSize;
  71. property MemPitch: NativeUInt read fDeviceProperties.MemPitch;
  72. property MaxThreadsPerBlock: Integer
  73. read fDeviceProperties.MaxThreadsPerBlock;
  74. property MaxThreadsDim: TCUDADimensions read fMaxThreadsDim;
  75. property MaxGridSize: TCUDADimensions read fMaxGridSize;
  76. property ClockRate: Integer read fDeviceProperties.ClockRate;
  77. property TotalConstMem: NativeUInt read fDeviceProperties.TotalConstMem;
  78. property Major: Integer read fDeviceProperties.Major;
  79. property Minor: Integer read fDeviceProperties.Minor;
  80. property TextureAlignment: NativeUInt read fDeviceProperties.TextureAlignment;
  81. property DeviceOverlap: Integer read fDeviceProperties.DeviceOverlap;
  82. property MultiProcessorCount: Integer
  83. read fDeviceProperties.MultiProcessorCount;
  84. end;
  85. TgxSCUDADevice = class(TComponent)
  86. private
  87. FSelectDeviceName: string;
  88. function GetDevice: TCUDADevice;
  89. procedure SetDevice(AValue: TCUDADevice);
  90. procedure SetDeviceName(const AName: string);
  91. public
  92. constructor Create(AOwner: TComponent); override;
  93. destructor Destroy; override;
  94. function Suitable: Boolean;
  95. published
  96. property SelectDevice: string read FSelectDeviceName write SetDeviceName;
  97. property Device: TCUDADevice read GetDevice write SetDevice;
  98. end;
  99. TCUDAHandlesMaster = class(TComponent)
  100. protected
  101. function GetContext: TCUDAContext; virtual; abstract;
  102. procedure AllocateHandles; virtual;
  103. procedure DestroyHandles; virtual;
  104. end;
  105. TCUDAHandleList = GThreadList<TCUDAHandlesMaster>;
  106. TCUDAContext = class(TObject)
  107. private
  108. fHandle: PCUcontext;
  109. FDevice: TCUDADevice;
  110. FOnOpenGLInteropInit: TOnOpenGLInteropInit;
  111. FHandleList: TCUDAHandleList;
  112. procedure SetDevice(ADevice: TCUDADevice);
  113. public
  114. constructor Create;
  115. destructor Destroy; override;
  116. // Destroy all handles based of this context.
  117. procedure DestroyAllHandles;
  118. // Pushes context onto CPU thread’s stack of current contexts.
  119. procedure Requires;
  120. // Pops context from current CPU thread.
  121. procedure Release;
  122. function IsValid: Boolean; inline;
  123. property Device: TCUDADevice read FDevice write SetDevice;
  124. property OnOpenGLInteropInit: TOnOpenGLInteropInit read FOnOpenGLInteropInit
  125. write FOnOpenGLInteropInit;
  126. end;
  127. TCUDADeviceList = GList<TCUDADevice>;
  128. TCUDAContextList = GList<TCUDAContext>;
  129. // Static class of CUDA contexts manager.
  130. CUDAContextManager = class
  131. private
  132. class var fDeviceList: TCUDADeviceList;
  133. class var
  134. fContextList: TCUDAContextList;
  135. class var FContextStacks: array of TCUDAContextList;
  136. protected
  137. class function GetDevice(i: Integer): TCUDADevice;
  138. class function GetNextUnusedDevice: TCUDADevice;
  139. class procedure RegisterContext(aContext: TCUDAContext);
  140. class procedure UnRegisterContext(aContext: TCUDAContext);
  141. class function GetThreadStack: TCUDAContextList;
  142. class function GetContext(i: Integer): TCUDAContext;
  143. public
  144. // Managment.
  145. class procedure Init;
  146. class procedure Done;
  147. class procedure CreateContext(aContext: TCUDAContext);
  148. class procedure DestroyContext(aContext: TCUDAContext);
  149. class procedure CreateContextOf(ADevice: TCUDADevice);
  150. class procedure DestroyContextOf(ADevice: TCUDADevice);
  151. class procedure PushContext(aContext: TCUDAContext);
  152. class function PopContext: TCUDAContext;
  153. // Fill unused device list to show its in property.
  154. class procedure FillUnusedDeviceList(var AList: TStringList);
  155. // Return device by name.
  156. class function GetDeviceByName(const AName: string): TCUDADevice;
  157. // Returns the number of CUDA compatiable devices.
  158. class function DeviceCount: Integer;
  159. // Access to devices list.
  160. property Devices[i: Integer]: TCUDADevice read GetDevice;
  161. // Returns a device that has a maximum Giga flops.
  162. class function GetMaxGflopsDevice: TCUDADevice;
  163. // Returns the number of TCUDAcontext object.
  164. class function ContextCount: Integer;
  165. // Return CUDA context of current thread.
  166. class function GetCurrentThreadContext: TCUDAContext;
  167. // Access to contexts list.
  168. property Contexts[i: Integer]: TCUDAContext read GetContext;
  169. end;
  170. //--------------------------------------------------------------
  171. implementation
  172. //--------------------------------------------------------------
  173. threadvar
  174. vStackIndex: Cardinal;
  175. // ------------------
  176. // ------------------ TCUDADimensions ------------------
  177. // ------------------
  178. constructor TCUDADimensions.Create(AOwner: TPersistent);
  179. const
  180. cXYZone: TDim3 = (1, 1, 1);
  181. cXYZmax: TDim3 = (MaxInt, MaxInt, MaxInt);
  182. begin
  183. inherited Create(AOwner);
  184. FReadOnly := False;
  185. FXYZ := cXYZone;
  186. FMaxXYZ := cXYZmax;
  187. end;
  188. procedure TCUDADimensions.Assign(Source: TPersistent);
  189. begin
  190. if Source is TCUDADimensions then
  191. begin
  192. FMaxXYZ[0] := TCUDADimensions(Source).FMaxXYZ[0];
  193. FMaxXYZ[1] := TCUDADimensions(Source).FMaxXYZ[1];
  194. FMaxXYZ[2] := TCUDADimensions(Source).FMaxXYZ[2];
  195. FXYZ[0] := TCUDADimensions(Source).FXYZ[0];
  196. FXYZ[1] := TCUDADimensions(Source).FXYZ[1];
  197. FXYZ[2] := TCUDADimensions(Source).FXYZ[2];
  198. NotifyChange(Self);
  199. end;
  200. inherited Assign(Source);
  201. end;
  202. function TCUDADimensions.GetDimComponent(index: Integer): Integer;
  203. begin
  204. Result := FXYZ[index];
  205. end;
  206. procedure TCUDADimensions.SetDimComponent(index: Integer; Value: Integer);
  207. var
  208. v: LongWord;
  209. begin
  210. if not FReadOnly then
  211. begin
  212. if Value < 1 then
  213. v := 1
  214. else
  215. v := LongWord(Value);
  216. if v > FMaxXYZ[index] then
  217. v := FMaxXYZ[index];
  218. FXYZ[index] := v;
  219. NotifyChange(Self);
  220. end;
  221. end;
  222. function TCUDADimensions.GetMaxDimComponent(index: Integer): Integer;
  223. begin
  224. Result := FMaxXYZ[index];
  225. end;
  226. procedure TCUDADimensions.SetMaxDimComponent(index: Integer; Value: Integer);
  227. begin
  228. if not FReadOnly then
  229. begin
  230. if Value > 0 then
  231. begin
  232. FMaxXYZ[index] := LongWord(Value);
  233. if FXYZ[index] > FMaxXYZ[index] then
  234. FXYZ[index] := FMaxXYZ[index];
  235. NotifyChange(Self);
  236. end;
  237. end;
  238. end;
  239. // ------------------
  240. // ------------------ TCUDADevice ------------------
  241. // ------------------
  242. constructor TCUDADevice.Create;
  243. begin
  244. fMaxThreadsDim := TCUDADimensions.Create(Self);
  245. fMaxThreadsDim.ReadOnlyValue := True;
  246. fMaxGridSize := TCUDADimensions.Create(Self);
  247. fMaxGridSize.ReadOnlyValue := True;
  248. if IsCUDAInitialized then
  249. begin
  250. fID := CUDAContextManager.fDeviceList.Count;
  251. FUsed := False;
  252. FSuitable := cuDeviceGet(fHandle, fID) = CUDA_SUCCESS;
  253. if FSuitable then
  254. begin
  255. cuDeviceGetName(@fDeviceProperties.name[0], SizeOf(fDeviceProperties.name), fHandle);
  256. cuDeviceTotalMem(@fDeviceProperties.TotalGlobalMem, fHandle);
  257. cuDeviceGetAttribute(@fDeviceProperties.SharedMemPerBlock, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK, fHandle);
  258. cuDeviceGetAttribute(@fDeviceProperties.RegsPerBlock, CU_DEVICE_ATTRIBUTE_MAX_REGISTERS_PER_BLOCK, fHandle);
  259. cuDeviceGetAttribute(@fDeviceProperties.WarpSize, CU_DEVICE_ATTRIBUTE_WARP_SIZE, fHandle);
  260. cuDeviceGetAttribute(@fDeviceProperties.MemPitch, CU_DEVICE_ATTRIBUTE_MAX_PITCH, fHandle);
  261. cuDeviceGetAttribute(@fDeviceProperties.MaxThreadsPerBlock, CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK, fHandle);
  262. cuDeviceGetAttribute(@fDeviceProperties.MaxThreadsDim[0], CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_X, fHandle);
  263. cuDeviceGetAttribute(@fDeviceProperties.MaxThreadsDim[1], CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Y, fHandle);
  264. cuDeviceGetAttribute(@fDeviceProperties.MaxThreadsDim[2], CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Z, fHandle);
  265. cuDeviceGetAttribute(@fDeviceProperties.MaxGridSize[0], CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_X, fHandle);
  266. cuDeviceGetAttribute(@fDeviceProperties.MaxGridSize[1], CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Y, fHandle);
  267. cuDeviceGetAttribute(@fDeviceProperties.MaxGridSize[2], CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Z, fHandle);
  268. cuDeviceGetAttribute(@fDeviceProperties.ClockRate, CU_DEVICE_ATTRIBUTE_CLOCK_RATE, fHandle);
  269. cuDeviceGetAttribute(@fDeviceProperties.TotalConstMem, CU_DEVICE_ATTRIBUTE_TOTAL_CONSTANT_MEMORY, fHandle);
  270. cuDeviceComputeCapability(fDeviceProperties.Major, fDeviceProperties.Minor, fHandle);
  271. cuDeviceGetAttribute(@fDeviceProperties.TextureAlignment, CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT, fHandle);
  272. cuDeviceGetAttribute(@fDeviceProperties.DeviceOverlap, CU_DEVICE_ATTRIBUTE_GPU_OVERLAP, fHandle);
  273. cuDeviceGetAttribute(@fDeviceProperties.DeviceOverlap, CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, fHandle);
  274. fGFlops := fDeviceProperties.MultiProcessorCount *
  275. fDeviceProperties.ClockRate;
  276. fMaxThreadsDim.FXYZ[0] := fDeviceProperties.MaxThreadsDim[0];
  277. fMaxThreadsDim.FXYZ[1] := fDeviceProperties.MaxThreadsDim[1];
  278. fMaxThreadsDim.FXYZ[2] := fDeviceProperties.MaxThreadsDim[2];
  279. fMaxGridSize.FXYZ[0] := fDeviceProperties.MaxGridSize[0];
  280. fMaxGridSize.FXYZ[1] := fDeviceProperties.MaxGridSize[1];
  281. fMaxGridSize.FXYZ[2] := fDeviceProperties.MaxGridSize[2];
  282. end;
  283. end;
  284. end;
  285. destructor TCUDADevice.Destroy;
  286. begin
  287. fMaxThreadsDim.Destroy;
  288. fMaxGridSize.Destroy;
  289. inherited;
  290. end;
  291. procedure TCUDADevice.Assign(Source: TPersistent);
  292. var
  293. dev: TCUDADevice;
  294. begin
  295. if Source is TCUDADevice then
  296. begin
  297. dev := TCUDADevice(Source);
  298. fID := dev.fID;
  299. fHandle := dev.fHandle;
  300. fGFlops := dev.fGFlops;
  301. fDeviceProperties := dev.fDeviceProperties;
  302. FSuitable := dev.FSuitable;
  303. fMaxThreadsDim.Assign(dev.fMaxThreadsDim);
  304. fMaxGridSize.Assign(dev.fMaxGridSize);
  305. end;
  306. inherited Assign(Source);
  307. end;
  308. function TCUDADevice.GetName: string;
  309. begin
  310. Result := Format('%s (%d)', [string(fDeviceProperties.name), fID + 1]);
  311. end;
  312. function TCUDADevice.TotalMemory: Cardinal;
  313. begin
  314. cuDeviceTotalMem(@fDeviceProperties.TotalGlobalMem, fHandle);
  315. Result := fDeviceProperties.TotalGlobalMem;
  316. end;
  317. // ------------------
  318. // ------------------ TgxSCUDADevice ------------------
  319. // ------------------
  320. constructor TgxSCUDADevice.Create(AOwner: TComponent);
  321. var
  322. LDevice: TCUDADevice;
  323. begin
  324. inherited Create(AOwner);
  325. LDevice := CUDAContextManager.GetNextUnusedDevice;
  326. if Assigned(LDevice) and LDevice.FSuitable then
  327. begin
  328. FSelectDeviceName := LDevice.name;
  329. LDevice.FUsed := True;
  330. end
  331. else
  332. begin
  333. FSelectDeviceName := '';
  334. end;
  335. end;
  336. destructor TgxSCUDADevice.Destroy;
  337. var
  338. Device: TCUDADevice;
  339. begin
  340. inherited;
  341. Device := CUDAContextManager.GetDeviceByName(FSelectDeviceName);
  342. if Assigned(Device) then
  343. Device.FUsed := False;
  344. end;
  345. function TgxSCUDADevice.GetDevice: TCUDADevice;
  346. begin
  347. Result := CUDAContextManager.GetDeviceByName(FSelectDeviceName);
  348. end;
  349. function TgxSCUDADevice.Suitable: Boolean;
  350. var
  351. LDevice: TCUDADevice;
  352. begin
  353. LDevice := GetDevice;
  354. Result := Assigned(LDevice);
  355. if Result then
  356. Result := LDevice.FSuitable;
  357. end;
  358. procedure TgxSCUDADevice.SetDevice(AValue: TCUDADevice);
  359. begin
  360. end;
  361. procedure TgxSCUDADevice.SetDeviceName(const AName: string);
  362. begin
  363. if FSelectDeviceName <> AName then
  364. begin
  365. CUDAContextManager.DestroyContextOf(Self.Device);
  366. FSelectDeviceName := AName;
  367. CUDAContextManager.CreateContextOf(Self.Device);
  368. end;
  369. end;
  370. // ------------------
  371. // ------------------ TCUDAContextManager ------------------
  372. // ------------------
  373. class procedure CUDAContextManager.Init;
  374. var
  375. dCount: Integer;
  376. status: TCUresult;
  377. i: Integer;
  378. begin
  379. if InitCUDA and not Assigned(fDeviceList) then
  380. begin
  381. fDeviceList := TCUDADeviceList.Create;
  382. fContextList := TCUDAContextList.Create;
  383. dCount := 0;
  384. status := cuInit(0);
  385. if status = CUDA_SUCCESS then
  386. cuDeviceGetCount(dCount);
  387. // Fill devices list
  388. for i := 0 to dCount - 1 do
  389. fDeviceList.Add(TCUDADevice.Create);
  390. end;
  391. end;
  392. class procedure CUDAContextManager.Done;
  393. var
  394. I, J: Integer;
  395. begin
  396. if Assigned(fDeviceList) then
  397. for i := 0 to fDeviceList.Count - 1 do
  398. fDeviceList[i].Free;
  399. for I := 0 to High(FContextStacks) do
  400. begin
  401. if FContextStacks[I].Count > 0 then
  402. begin
  403. ShowMessage(strUnbalansedUsage);
  404. for J := FContextStacks[I].Count - 1 to 0 do
  405. FContextStacks[I][J].Release;
  406. end;
  407. FContextStacks[I].Destroy;
  408. end;
  409. fDeviceList.Free;
  410. fContextList.Free;
  411. CloseCUDA;
  412. end;
  413. class procedure CUDAContextManager.RegisterContext(aContext: TCUDAContext);
  414. begin
  415. if fContextList.IndexOf(aContext) >= 0 then
  416. begin
  417. ShowMessage(strInvalidContextReg);
  418. Abort;
  419. end
  420. else
  421. fContextList.Add(aContext);
  422. end;
  423. class procedure CUDAContextManager.UnRegisterContext(aContext: TCUDAContext);
  424. begin
  425. if fContextList.IndexOf(aContext) < 0 then
  426. begin
  427. ShowMessage(strInvalidContextReg);
  428. Abort;
  429. end
  430. else
  431. begin
  432. fContextList.Remove(aContext);
  433. end;
  434. end;
  435. class function CUDAContextManager.ContextCount: Integer;
  436. begin
  437. Result := fContextList.Count;
  438. end;
  439. class function CUDAContextManager.DeviceCount: Integer;
  440. begin
  441. Result := fDeviceList.Count;
  442. end;
  443. class function CUDAContextManager.GetDevice(i: Integer): TCUDADevice;
  444. begin
  445. Result := nil;
  446. if i < fDeviceList.Count then
  447. Result := fDeviceList[i];
  448. end;
  449. class function CUDAContextManager.GetContext(i: Integer): TCUDAContext;
  450. begin
  451. Result := nil;
  452. if i < fContextList.Count then
  453. Result := fContextList[i];
  454. end;
  455. class procedure CUDAContextManager.FillUnusedDeviceList(var AList: TStringList);
  456. var
  457. i: Integer;
  458. begin
  459. if not Assigned(AList) then
  460. AList := TStringList.Create
  461. else
  462. AList.Clear;
  463. for i := 0 to fDeviceList.Count - 1 do
  464. if not fDeviceList[i].FUsed then
  465. AList.Add(fDeviceList[i].name);
  466. end;
  467. class function CUDAContextManager.GetDeviceByName(const AName: string)
  468. : TCUDADevice;
  469. var
  470. i: Integer;
  471. Device: TCUDADevice;
  472. begin
  473. Result := nil;
  474. if Length(AName) = 0 then
  475. exit;
  476. for i := 0 to fDeviceList.Count - 1 do
  477. begin
  478. Device := fDeviceList[i];
  479. if Device.name = AName then
  480. begin
  481. Result := Device;
  482. exit;
  483. end;
  484. end;
  485. end;
  486. class function CUDAContextManager.GetMaxGflopsDevice: TCUDADevice;
  487. var
  488. max_gflops: Integer;
  489. i: Integer;
  490. Device: TCUDADevice;
  491. begin
  492. Device := nil;
  493. max_gflops := 0;
  494. for i := 0 to fDeviceList.Count - 1 do
  495. begin
  496. if max_gflops < fDeviceList.Items[i].fGFlops then
  497. begin
  498. Device := fDeviceList.Items[i];
  499. max_gflops := Device.fGFlops;
  500. end;
  501. end;
  502. Result := Device;
  503. end;
  504. class function CUDAContextManager.GetNextUnusedDevice: TCUDADevice;
  505. var
  506. i: Integer;
  507. Device: TCUDADevice;
  508. begin
  509. Result := nil;
  510. for i := 0 to fDeviceList.Count - 1 do
  511. begin
  512. Device := fDeviceList[i];
  513. if not Device.FUsed then
  514. begin
  515. Result := Device;
  516. exit;
  517. end;
  518. end;
  519. end;
  520. class procedure CUDAContextManager.CreateContext(aContext: TCUDAContext);
  521. var
  522. status: TCUresult;
  523. cuOldContext, cuContext: PCUcontext;
  524. LGLContext: TgxContext;
  525. LStack: TCUDAContextList;
  526. begin
  527. if not Assigned(aContext.FDevice)
  528. or not aContext.FDevice.FSuitable then
  529. begin
  530. ShowMessage(strNoDeviceToCreate);
  531. Abort;
  532. end;
  533. if GetThreadStack.Count > 0 then
  534. begin
  535. if cuCtxPopCurrent(cuOldContext) <> CUDA_SUCCESS then
  536. begin
  537. ShowMessage(strThreadBusy);
  538. Abort;
  539. end;
  540. end
  541. else
  542. cuOldContext := nil;
  543. if aContext.IsValid then
  544. DestroyContext(aContext);
  545. RegisterContext(aContext);
  546. status := CUDA_SUCCESS;
  547. if Assigned(aContext.FOnOpenGLInteropInit) then
  548. begin
  549. aContext.FOnOpenGLInteropInit(LGLContext);
  550. if Assigned(LGLContext) and LGLContext.IsValid then
  551. begin
  552. LGLContext.Activate;
  553. cuContext := nil;
  554. status := cuGLCtxCreate(cuContext, 0, aContext.FDevice.fHandle);
  555. LGLContext.Deactivate;
  556. end
  557. else
  558. begin
  559. ShowMessage(strInvalidGLContext);
  560. UnRegisterContext(aContext);
  561. Abort;
  562. end;
  563. end
  564. else
  565. begin
  566. status := cuCtxCreate(cuContext, 0, aContext.FDevice.fHandle);
  567. end;
  568. if (status <> CUDA_SUCCESS) then
  569. begin
  570. ShowMessage(cudaGetLastErrorString);
  571. UnRegisterContext(aContext);
  572. cuCtxDetach(cuContext);
  573. Abort;
  574. end;
  575. aContext.fHandle := cuContext;
  576. // Make context be floating to use it in different thread
  577. if cuCtxPopCurrent(cuContext) <> CUDA_SUCCESS then
  578. begin
  579. LStack := GetThreadStack;
  580. LStack.Insert(LStack.Count - 1, aContext);
  581. ShowMessage(strMakeFloatingFail);
  582. end;
  583. if Assigned(cuOldContext) then
  584. cuCtxPushCurrent(cuOldContext);
  585. end;
  586. class procedure CUDAContextManager.CreateContextOf(ADevice: TCUDADevice);
  587. var
  588. i: Integer;
  589. begin
  590. if Assigned(ADevice) and ADevice.FSuitable then
  591. begin
  592. for i := 0 to fContextList.Count do
  593. if fContextList[i].FDevice = ADevice then
  594. CreateContext(fContextList[i]);
  595. end;
  596. end;
  597. class procedure CUDAContextManager.DestroyContext(aContext: TCUDAContext);
  598. begin
  599. if aContext.IsValid then
  600. begin
  601. aContext.DestroyAllHandles;
  602. cuCtxDestroy(aContext.fHandle);
  603. aContext.fHandle := nil;
  604. CUDAContextManager.UnRegisterContext(aContext);
  605. end;
  606. end;
  607. class procedure CUDAContextManager.DestroyContextOf(ADevice: TCUDADevice);
  608. var
  609. i: Integer;
  610. begin
  611. if Assigned(ADevice) and ADevice.FSuitable then
  612. begin
  613. for i := 0 to fContextList.Count - 1 do
  614. if fContextList[i].FDevice = ADevice then
  615. DestroyContext(fContextList[i]);
  616. end;
  617. end;
  618. class function CUDAContextManager.GetThreadStack: TCUDAContextList;
  619. begin
  620. if vStackIndex = 0 then
  621. begin
  622. SetLength(FContextStacks, Length(FContextStacks)+1);
  623. FContextStacks[High(FContextStacks)] := TCUDAContextList.Create;
  624. vStackIndex := High(FContextStacks)+1;
  625. end;
  626. Result := FContextStacks[vStackIndex-1];
  627. end;
  628. class function CUDAContextManager.GetCurrentThreadContext: TCUDAContext;
  629. begin
  630. if GetThreadStack.Count > 0 then
  631. Result := GetThreadStack.Last
  632. else
  633. Result := nil;
  634. end;
  635. class procedure CUDAContextManager.PushContext(aContext: TCUDAContext);
  636. var
  637. LContext: TCUDAContext;
  638. cuContext: PCUcontext;
  639. begin
  640. LContext := GetCurrentThreadContext;
  641. if LContext <> aContext then
  642. begin
  643. // Pop current
  644. if Assigned(LContext) then
  645. if cuCtxPopCurrent(cuContext) = CUDA_SUCCESS then
  646. begin
  647. if LContext.fHandle <> cuContext then
  648. begin
  649. ShowMessage(strUnbalansedUsage);
  650. Abort;
  651. end;
  652. end
  653. else
  654. Abort;
  655. // Push required
  656. if cuCtxPushCurrent(aContext.fHandle) <> CUDA_SUCCESS then
  657. Abort;
  658. end;
  659. GetThreadStack.Add(aContext);
  660. end;
  661. class function CUDAContextManager.PopContext: TCUDAContext;
  662. var
  663. C: Integer;
  664. LContext: TCUDAContext;
  665. cuContext: PCUcontext;
  666. begin
  667. C := GetThreadStack.Count;
  668. if C = 0 then
  669. begin
  670. ShowMessage(strUnbalansedUsage);
  671. Abort;
  672. end;
  673. Result := GetThreadStack.Last;
  674. GetThreadStack.Delete(C - 1);
  675. LContext := GetCurrentThreadContext;
  676. if Result <> LContext then
  677. begin
  678. if cuCtxPopCurrent(cuContext) = CUDA_SUCCESS then
  679. begin
  680. if Result.fHandle <> cuContext then
  681. begin
  682. ShowMessage(strUnbalansedUsage);
  683. Abort;
  684. end;
  685. end
  686. else
  687. Abort;
  688. if Assigned(LContext)
  689. and (cuCtxPushCurrent(LContext.fHandle) <> CUDA_SUCCESS) then
  690. Abort;
  691. end;
  692. end;
  693. // ------------------
  694. // ------------------ TCUDAHandlesMaster ------------------
  695. // ------------------
  696. procedure TCUDAHandlesMaster.AllocateHandles;
  697. var
  698. LList: TCUDAHandleList.TLockableList;
  699. begin
  700. LList := GetContext.FHandleList.LockList;
  701. if LList.IndexOf(Self) < 0 then
  702. LList.Add(Self);
  703. GetContext.FHandleList.UnlockList;
  704. end;
  705. procedure TCUDAHandlesMaster.DestroyHandles;
  706. begin
  707. GetContext.FHandleList.Remove(Self);
  708. end;
  709. // ------------------
  710. // ------------------ TCUDAContext ------------------
  711. // ------------------
  712. constructor TCUDAContext.Create;
  713. begin
  714. inherited Create;
  715. fHandle := nil;
  716. FDevice := nil;
  717. FHandleList := TCUDAHandleList.Create;
  718. end;
  719. destructor TCUDAContext.Destroy;
  720. begin
  721. DestroyAllHandles;
  722. CUDAContextManager.DestroyContext(Self);
  723. FHandleList.Destroy;
  724. inherited;
  725. end;
  726. procedure TCUDAContext.SetDevice(ADevice: TCUDADevice);
  727. begin
  728. if FDevice <> ADevice then
  729. begin
  730. CUDAContextManager.DestroyContext(Self);
  731. FDevice := ADevice;
  732. end;
  733. end;
  734. procedure TCUDAContext.Requires;
  735. begin
  736. if not IsValid then
  737. begin
  738. ShowMessage(strContextNotInit);
  739. Abort;
  740. end;
  741. CUDAContextManager.PushContext(Self);
  742. end;
  743. procedure TCUDAContext.Release;
  744. begin
  745. CUDAContextManager.PopContext;
  746. end;
  747. procedure TCUDAContext.DestroyAllHandles;
  748. var
  749. i: Integer;
  750. LList: TCUDAHandleList.TLockableList;
  751. begin
  752. Requires;
  753. LList := FHandleList.LockList;
  754. try
  755. for i := LList.Count - 1 downto 0 do
  756. LList[i].DestroyHandles;
  757. finally
  758. FHandleList.Clear;
  759. FHandleList.UnlockList;
  760. Release;
  761. end;
  762. end;
  763. function TCUDAContext.IsValid: Boolean;
  764. begin
  765. Result := Assigned(fHandle);
  766. end;
  767. // ------------------------------------------------------------------
  768. initialization
  769. // ------------------------------------------------------------------
  770. RegisterClasses([TgxSCUDADevice]);
  771. CUDAContextManager.Init;
  772. finalization
  773. CUDAContextManager.Done;
  774. end.