CUDA.Context.pas 23 KB

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