ShaderCollection.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.GAL;
  3. using Silk.NET.Vulkan;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Collections.ObjectModel;
  7. using System.Linq;
  8. using System.Threading.Tasks;
  9. namespace Ryujinx.Graphics.Vulkan
  10. {
  11. class ShaderCollection : IProgram
  12. {
  13. private readonly PipelineShaderStageCreateInfo[] _infos;
  14. private readonly Shader[] _shaders;
  15. private readonly PipelineLayoutCacheEntry _plce;
  16. public PipelineLayout PipelineLayout => _plce.PipelineLayout;
  17. public bool HasMinimalLayout { get; }
  18. public bool UsePushDescriptors { get; }
  19. public bool IsCompute { get; }
  20. public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0;
  21. public bool UpdateTexturesWithoutTemplate { get; }
  22. public uint Stages { get; }
  23. public PipelineStageFlags IncoherentBufferWriteStages { get; }
  24. public PipelineStageFlags IncoherentTextureWriteStages { get; }
  25. public ResourceBindingSegment[][] ClearSegments { get; }
  26. public ResourceBindingSegment[][] BindingSegments { get; }
  27. public DescriptorSetTemplate[] Templates { get; }
  28. public ProgramLinkStatus LinkStatus { get; private set; }
  29. public readonly SpecDescription[] SpecDescriptions;
  30. public bool IsLinked
  31. {
  32. get
  33. {
  34. if (LinkStatus == ProgramLinkStatus.Incomplete)
  35. {
  36. CheckProgramLink(true);
  37. }
  38. return LinkStatus == ProgramLinkStatus.Success;
  39. }
  40. }
  41. private HashTableSlim<PipelineUid, Auto<DisposablePipeline>> _graphicsPipelineCache;
  42. private HashTableSlim<SpecData, Auto<DisposablePipeline>> _computePipelineCache;
  43. private readonly VulkanRenderer _gd;
  44. private Device _device;
  45. private bool _initialized;
  46. private ProgramPipelineState _state;
  47. private DisposableRenderPass _dummyRenderPass;
  48. private readonly Task _compileTask;
  49. private bool _firstBackgroundUse;
  50. public ShaderCollection(
  51. VulkanRenderer gd,
  52. Device device,
  53. ShaderSource[] shaders,
  54. ResourceLayout resourceLayout,
  55. SpecDescription[] specDescription = null,
  56. bool isMinimal = false)
  57. {
  58. _gd = gd;
  59. _device = device;
  60. if (specDescription != null && specDescription.Length != shaders.Length)
  61. {
  62. throw new ArgumentException($"{nameof(specDescription)} array length must match {nameof(shaders)} array if provided");
  63. }
  64. gd.Shaders.Add(this);
  65. Shader[] internalShaders = new Shader[shaders.Length];
  66. _infos = new PipelineShaderStageCreateInfo[shaders.Length];
  67. SpecDescriptions = specDescription;
  68. LinkStatus = ProgramLinkStatus.Incomplete;
  69. uint stages = 0;
  70. for (int i = 0; i < shaders.Length; i++)
  71. {
  72. Shader shader = new(gd.Api, device, shaders[i]);
  73. stages |= 1u << shader.StageFlags switch
  74. {
  75. ShaderStageFlags.FragmentBit => 1,
  76. ShaderStageFlags.GeometryBit => 2,
  77. ShaderStageFlags.TessellationControlBit => 3,
  78. ShaderStageFlags.TessellationEvaluationBit => 4,
  79. _ => 0,
  80. };
  81. if (shader.StageFlags == ShaderStageFlags.ComputeBit)
  82. {
  83. IsCompute = true;
  84. }
  85. internalShaders[i] = shader;
  86. }
  87. _shaders = internalShaders;
  88. bool usePushDescriptors = !isMinimal &&
  89. VulkanConfiguration.UsePushDescriptors &&
  90. _gd.Capabilities.SupportsPushDescriptors &&
  91. !IsCompute &&
  92. !HasPushDescriptorsBug(gd) &&
  93. CanUsePushDescriptors(gd, resourceLayout, IsCompute);
  94. ReadOnlyCollection<ResourceDescriptorCollection> sets = usePushDescriptors ?
  95. BuildPushDescriptorSets(gd, resourceLayout.Sets) : resourceLayout.Sets;
  96. _plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, sets, usePushDescriptors);
  97. HasMinimalLayout = isMinimal;
  98. UsePushDescriptors = usePushDescriptors;
  99. Stages = stages;
  100. ClearSegments = BuildClearSegments(sets);
  101. BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures);
  102. Templates = BuildTemplates(usePushDescriptors);
  103. (IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages);
  104. // Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
  105. UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures;
  106. _compileTask = Task.CompletedTask;
  107. _firstBackgroundUse = false;
  108. }
  109. public ShaderCollection(
  110. VulkanRenderer gd,
  111. Device device,
  112. ShaderSource[] sources,
  113. ResourceLayout resourceLayout,
  114. ProgramPipelineState state,
  115. bool fromCache) : this(gd, device, sources, resourceLayout)
  116. {
  117. _state = state;
  118. _compileTask = BackgroundCompilation();
  119. _firstBackgroundUse = !fromCache;
  120. }
  121. private static bool HasPushDescriptorsBug(VulkanRenderer gd)
  122. {
  123. // Those GPUs/drivers do not work properly with push descriptors, so we must force disable them.
  124. return gd.IsNvidiaPreTuring || (gd.IsIntelArc && gd.IsIntelWindows);
  125. }
  126. private static bool CanUsePushDescriptors(VulkanRenderer gd, ResourceLayout layout, bool isCompute)
  127. {
  128. // If binding 3 is immediately used, use an alternate set of reserved bindings.
  129. ReadOnlyCollection<ResourceUsage> uniformUsage = layout.SetUsages[0].Usages;
  130. bool hasBinding3 = uniformUsage.Any(x => x.Binding == 3);
  131. int[] reserved = isCompute ? [] : gd.GetPushDescriptorReservedBindings(hasBinding3);
  132. // Can't use any of the reserved usages.
  133. for (int i = 0; i < uniformUsage.Count; i++)
  134. {
  135. int binding = uniformUsage[i].Binding;
  136. if (reserved.Contains(binding) ||
  137. binding >= Constants.MaxPushDescriptorBinding ||
  138. binding >= gd.Capabilities.MaxPushDescriptors + reserved.Count(id => id < binding))
  139. {
  140. return false;
  141. }
  142. }
  143. //Prevent the sum of descriptors from exceeding MaxPushDescriptors
  144. int totalDescriptors = 0;
  145. foreach (ResourceDescriptor desc in layout.Sets.First().Descriptors)
  146. {
  147. if (!reserved.Contains(desc.Binding))
  148. totalDescriptors += desc.Count;
  149. }
  150. if (totalDescriptors > gd.Capabilities.MaxPushDescriptors)
  151. return false;
  152. return true;
  153. }
  154. private static ReadOnlyCollection<ResourceDescriptorCollection> BuildPushDescriptorSets(
  155. VulkanRenderer gd,
  156. ReadOnlyCollection<ResourceDescriptorCollection> sets)
  157. {
  158. // The reserved bindings were selected when determining if push descriptors could be used.
  159. int[] reserved = gd.GetPushDescriptorReservedBindings(false);
  160. ResourceDescriptorCollection[] result = new ResourceDescriptorCollection[sets.Count];
  161. for (int i = 0; i < sets.Count; i++)
  162. {
  163. if (i == 0)
  164. {
  165. // Push descriptors apply here. Remove reserved bindings.
  166. ResourceDescriptorCollection original = sets[i];
  167. ResourceDescriptor[] pdUniforms = new ResourceDescriptor[original.Descriptors.Count];
  168. int j = 0;
  169. foreach (ResourceDescriptor descriptor in original.Descriptors)
  170. {
  171. if (reserved.Contains(descriptor.Binding))
  172. {
  173. // If the binding is reserved, set its descriptor count to 0.
  174. pdUniforms[j++] = new ResourceDescriptor(
  175. descriptor.Binding,
  176. 0,
  177. descriptor.Type,
  178. descriptor.Stages);
  179. }
  180. else
  181. {
  182. pdUniforms[j++] = descriptor;
  183. }
  184. }
  185. result[i] = new ResourceDescriptorCollection(new(pdUniforms));
  186. }
  187. else
  188. {
  189. result[i] = sets[i];
  190. }
  191. }
  192. return new(result);
  193. }
  194. private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection<ResourceDescriptorCollection> sets)
  195. {
  196. ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][];
  197. for (int setIndex = 0; setIndex < sets.Count; setIndex++)
  198. {
  199. List<ResourceBindingSegment> currentSegments = [];
  200. ResourceDescriptor currentDescriptor = default;
  201. int currentCount = 0;
  202. for (int index = 0; index < sets[setIndex].Descriptors.Count; index++)
  203. {
  204. ResourceDescriptor descriptor = sets[setIndex].Descriptors[index];
  205. if (currentDescriptor.Binding + currentCount != descriptor.Binding ||
  206. currentDescriptor.Type != descriptor.Type ||
  207. currentDescriptor.Stages != descriptor.Stages ||
  208. currentDescriptor.Count > 1 ||
  209. descriptor.Count > 1)
  210. {
  211. if (currentCount != 0)
  212. {
  213. currentSegments.Add(new ResourceBindingSegment(
  214. currentDescriptor.Binding,
  215. currentCount,
  216. currentDescriptor.Type,
  217. currentDescriptor.Stages,
  218. currentDescriptor.Count > 1));
  219. }
  220. currentDescriptor = descriptor;
  221. currentCount = descriptor.Count;
  222. }
  223. else
  224. {
  225. currentCount += descriptor.Count;
  226. }
  227. }
  228. if (currentCount != 0)
  229. {
  230. currentSegments.Add(new ResourceBindingSegment(
  231. currentDescriptor.Binding,
  232. currentCount,
  233. currentDescriptor.Type,
  234. currentDescriptor.Stages,
  235. currentDescriptor.Count > 1));
  236. }
  237. segments[setIndex] = currentSegments.ToArray();
  238. }
  239. return segments;
  240. }
  241. private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages, out bool usesBufferTextures)
  242. {
  243. usesBufferTextures = false;
  244. ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
  245. for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
  246. {
  247. List<ResourceBindingSegment> currentSegments = [];
  248. ResourceUsage currentUsage = default;
  249. int currentCount = 0;
  250. for (int index = 0; index < setUsages[setIndex].Usages.Count; index++)
  251. {
  252. ResourceUsage usage = setUsages[setIndex].Usages[index];
  253. if (usage.Type == ResourceType.BufferTexture)
  254. {
  255. usesBufferTextures = true;
  256. }
  257. if (currentUsage.Binding + currentCount != usage.Binding ||
  258. currentUsage.Type != usage.Type ||
  259. currentUsage.Stages != usage.Stages ||
  260. currentUsage.ArrayLength > 1 ||
  261. usage.ArrayLength > 1)
  262. {
  263. if (currentCount != 0)
  264. {
  265. currentSegments.Add(new ResourceBindingSegment(
  266. currentUsage.Binding,
  267. currentCount,
  268. currentUsage.Type,
  269. currentUsage.Stages,
  270. currentUsage.ArrayLength > 1));
  271. }
  272. currentUsage = usage;
  273. currentCount = usage.ArrayLength;
  274. }
  275. else
  276. {
  277. currentCount++;
  278. }
  279. }
  280. if (currentCount != 0)
  281. {
  282. currentSegments.Add(new ResourceBindingSegment(
  283. currentUsage.Binding,
  284. currentCount,
  285. currentUsage.Type,
  286. currentUsage.Stages,
  287. currentUsage.ArrayLength > 1));
  288. }
  289. segments[setIndex] = currentSegments.ToArray();
  290. }
  291. return segments;
  292. }
  293. private DescriptorSetTemplate[] BuildTemplates(bool usePushDescriptors)
  294. {
  295. DescriptorSetTemplate[] templates = new DescriptorSetTemplate[BindingSegments.Length];
  296. for (int setIndex = 0; setIndex < BindingSegments.Length; setIndex++)
  297. {
  298. if (usePushDescriptors && setIndex == 0)
  299. {
  300. // Push descriptors get updated using templates owned by the pipeline layout.
  301. continue;
  302. }
  303. ResourceBindingSegment[] segments = BindingSegments[setIndex];
  304. if (segments != null && segments.Length > 0)
  305. {
  306. templates[setIndex] = new DescriptorSetTemplate(
  307. _gd,
  308. _device,
  309. segments,
  310. _plce,
  311. IsCompute ? PipelineBindPoint.Compute : PipelineBindPoint.Graphics,
  312. setIndex);
  313. }
  314. }
  315. return templates;
  316. }
  317. private PipelineStageFlags GetPipelineStages(ResourceStages stages)
  318. {
  319. PipelineStageFlags result = 0;
  320. if ((stages & ResourceStages.Compute) != 0)
  321. {
  322. result |= PipelineStageFlags.ComputeShaderBit;
  323. }
  324. if ((stages & ResourceStages.Vertex) != 0)
  325. {
  326. result |= PipelineStageFlags.VertexShaderBit;
  327. }
  328. if ((stages & ResourceStages.Fragment) != 0)
  329. {
  330. result |= PipelineStageFlags.FragmentShaderBit;
  331. }
  332. if ((stages & ResourceStages.Geometry) != 0)
  333. {
  334. result |= PipelineStageFlags.GeometryShaderBit;
  335. }
  336. if ((stages & ResourceStages.TessellationControl) != 0)
  337. {
  338. result |= PipelineStageFlags.TessellationControlShaderBit;
  339. }
  340. if ((stages & ResourceStages.TessellationEvaluation) != 0)
  341. {
  342. result |= PipelineStageFlags.TessellationEvaluationShaderBit;
  343. }
  344. return result;
  345. }
  346. private (PipelineStageFlags Buffer, PipelineStageFlags Texture) BuildIncoherentStages(ReadOnlyCollection<ResourceUsageCollection> setUsages)
  347. {
  348. PipelineStageFlags buffer = PipelineStageFlags.None;
  349. PipelineStageFlags texture = PipelineStageFlags.None;
  350. foreach (ResourceUsageCollection set in setUsages)
  351. {
  352. foreach (ResourceUsage range in set.Usages)
  353. {
  354. if (range.Write)
  355. {
  356. PipelineStageFlags stages = GetPipelineStages(range.Stages);
  357. switch (range.Type)
  358. {
  359. case ResourceType.Image:
  360. texture |= stages;
  361. break;
  362. case ResourceType.StorageBuffer:
  363. case ResourceType.BufferImage:
  364. buffer |= stages;
  365. break;
  366. }
  367. }
  368. }
  369. }
  370. return (buffer, texture);
  371. }
  372. private async Task BackgroundCompilation()
  373. {
  374. await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));
  375. if (Array.Exists(_shaders, shader => shader.CompileStatus == ProgramLinkStatus.Failure))
  376. {
  377. LinkStatus = ProgramLinkStatus.Failure;
  378. return;
  379. }
  380. try
  381. {
  382. if (IsCompute)
  383. {
  384. CreateBackgroundComputePipeline();
  385. }
  386. else
  387. {
  388. CreateBackgroundGraphicsPipeline();
  389. }
  390. }
  391. catch (VulkanException e)
  392. {
  393. Logger.Error?.PrintMsg(LogClass.Gpu, $"Background Compilation failed: {e.Message}");
  394. LinkStatus = ProgramLinkStatus.Failure;
  395. }
  396. }
  397. private void EnsureShadersReady()
  398. {
  399. if (!_initialized)
  400. {
  401. CheckProgramLink(true);
  402. ProgramLinkStatus resultStatus = ProgramLinkStatus.Success;
  403. for (int i = 0; i < _shaders.Length; i++)
  404. {
  405. Shader shader = _shaders[i];
  406. if (shader.CompileStatus != ProgramLinkStatus.Success)
  407. {
  408. resultStatus = ProgramLinkStatus.Failure;
  409. }
  410. _infos[i] = shader.GetInfo();
  411. }
  412. // If the link status was already set as failure by background compilation, prefer that decision.
  413. if (LinkStatus != ProgramLinkStatus.Failure)
  414. {
  415. LinkStatus = resultStatus;
  416. }
  417. _initialized = true;
  418. }
  419. }
  420. public PipelineShaderStageCreateInfo[] GetInfos()
  421. {
  422. EnsureShadersReady();
  423. return _infos;
  424. }
  425. protected DisposableRenderPass CreateDummyRenderPass()
  426. {
  427. if (_dummyRenderPass.Value.Handle != 0)
  428. {
  429. return _dummyRenderPass;
  430. }
  431. return _dummyRenderPass = _state.ToRenderPass(_gd, _device);
  432. }
  433. public void CreateBackgroundComputePipeline()
  434. {
  435. PipelineState pipeline = new();
  436. pipeline.Initialize();
  437. pipeline.Stages[0] = _shaders[0].GetInfo();
  438. pipeline.StagesCount = 1;
  439. pipeline.PipelineLayout = PipelineLayout;
  440. pipeline.CreateComputePipeline(_gd, _device, this, (_gd.Pipeline as PipelineBase).PipelineCache);
  441. pipeline.Dispose();
  442. }
  443. public void CreateBackgroundGraphicsPipeline()
  444. {
  445. // To compile shaders in the background in Vulkan, we need to create valid pipelines using the shader modules.
  446. // The GPU provides pipeline state via the GAL that can be converted into our internal Vulkan pipeline state.
  447. // This should match the pipeline state at the time of the first draw. If it doesn't, then it'll likely be
  448. // close enough that the GPU driver will reuse the compiled shader for the different state.
  449. // First, we need to create a render pass object compatible with the one that will be used at runtime.
  450. // The active attachment formats have been provided by the abstraction layer.
  451. DisposableRenderPass renderPass = CreateDummyRenderPass();
  452. PipelineState pipeline = _state.ToVulkanPipelineState(_gd);
  453. // Copy the shader stage info to the pipeline.
  454. Span<PipelineShaderStageCreateInfo> stages = pipeline.Stages.AsSpan();
  455. for (int i = 0; i < _shaders.Length; i++)
  456. {
  457. stages[i] = _shaders[i].GetInfo();
  458. }
  459. pipeline.HasTessellationControlShader = HasTessellationControlShader;
  460. pipeline.StagesCount = (uint)_shaders.Length;
  461. pipeline.PipelineLayout = PipelineLayout;
  462. pipeline.CreateGraphicsPipeline(_gd, _device, this, (_gd.Pipeline as PipelineBase).PipelineCache, renderPass.Value, throwOnError: true);
  463. pipeline.Dispose();
  464. }
  465. public ProgramLinkStatus CheckProgramLink(bool blocking)
  466. {
  467. if (LinkStatus == ProgramLinkStatus.Incomplete)
  468. {
  469. ProgramLinkStatus resultStatus = ProgramLinkStatus.Success;
  470. foreach (Shader shader in _shaders)
  471. {
  472. if (shader.CompileStatus == ProgramLinkStatus.Incomplete)
  473. {
  474. if (blocking)
  475. {
  476. // Wait for this shader to finish compiling.
  477. shader.WaitForCompile();
  478. if (shader.CompileStatus != ProgramLinkStatus.Success)
  479. {
  480. resultStatus = ProgramLinkStatus.Failure;
  481. }
  482. }
  483. else
  484. {
  485. return ProgramLinkStatus.Incomplete;
  486. }
  487. }
  488. }
  489. if (!_compileTask.IsCompleted)
  490. {
  491. if (blocking)
  492. {
  493. _compileTask.Wait();
  494. if (LinkStatus == ProgramLinkStatus.Failure)
  495. {
  496. return ProgramLinkStatus.Failure;
  497. }
  498. }
  499. else
  500. {
  501. return ProgramLinkStatus.Incomplete;
  502. }
  503. }
  504. return resultStatus;
  505. }
  506. return LinkStatus;
  507. }
  508. public byte[] GetBinary()
  509. {
  510. return null;
  511. }
  512. public DescriptorSetTemplate GetPushDescriptorTemplate(long updateMask)
  513. {
  514. return _plce.GetPushDescriptorTemplate(IsCompute ? PipelineBindPoint.Compute : PipelineBindPoint.Graphics, updateMask);
  515. }
  516. public void AddComputePipeline(ref SpecData key, Auto<DisposablePipeline> pipeline)
  517. {
  518. (_computePipelineCache ??= new()).Add(ref key, pipeline);
  519. }
  520. public void AddGraphicsPipeline(ref PipelineUid key, Auto<DisposablePipeline> pipeline)
  521. {
  522. (_graphicsPipelineCache ??= new()).Add(ref key, pipeline);
  523. }
  524. public bool TryGetComputePipeline(ref SpecData key, out Auto<DisposablePipeline> pipeline)
  525. {
  526. if (_computePipelineCache == null)
  527. {
  528. pipeline = default;
  529. return false;
  530. }
  531. if (_computePipelineCache.TryGetValue(ref key, out pipeline))
  532. {
  533. return true;
  534. }
  535. return false;
  536. }
  537. public bool TryGetGraphicsPipeline(ref PipelineUid key, out Auto<DisposablePipeline> pipeline)
  538. {
  539. if (_graphicsPipelineCache == null)
  540. {
  541. pipeline = default;
  542. return false;
  543. }
  544. if (!_graphicsPipelineCache.TryGetValue(ref key, out pipeline))
  545. {
  546. if (_firstBackgroundUse)
  547. {
  548. Logger.Warning?.Print(LogClass.Gpu, "Background pipeline compile missed on draw - incorrect pipeline state?");
  549. _firstBackgroundUse = false;
  550. }
  551. return false;
  552. }
  553. _firstBackgroundUse = false;
  554. return true;
  555. }
  556. public void UpdateDescriptorCacheCommandBufferIndex(int commandBufferIndex)
  557. {
  558. _plce.UpdateCommandBufferIndex(commandBufferIndex);
  559. }
  560. public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(int setIndex, out bool isNew)
  561. {
  562. return _plce.GetNewDescriptorSetCollection(setIndex, out isNew);
  563. }
  564. public Auto<DescriptorSetCollection> GetNewManualDescriptorSetCollection(CommandBufferScoped cbs, int setIndex, out int cacheIndex)
  565. {
  566. return _plce.GetNewManualDescriptorSetCollection(cbs, setIndex, out cacheIndex);
  567. }
  568. public void UpdateManualDescriptorSetCollectionOwnership(CommandBufferScoped cbs, int setIndex, int cacheIndex)
  569. {
  570. _plce.UpdateManualDescriptorSetCollectionOwnership(cbs, setIndex, cacheIndex);
  571. }
  572. public void ReleaseManualDescriptorSetCollection(int setIndex, int cacheIndex)
  573. {
  574. _plce.ReleaseManualDescriptorSetCollection(setIndex, cacheIndex);
  575. }
  576. public bool HasSameLayout(ShaderCollection other)
  577. {
  578. return other != null && _plce == other._plce;
  579. }
  580. protected virtual void Dispose(bool disposing)
  581. {
  582. if (disposing)
  583. {
  584. if (!_gd.Shaders.Remove(this))
  585. {
  586. return;
  587. }
  588. for (int i = 0; i < _shaders.Length; i++)
  589. {
  590. _shaders[i].Dispose();
  591. }
  592. if (_graphicsPipelineCache != null)
  593. {
  594. foreach (Auto<DisposablePipeline> pipeline in _graphicsPipelineCache.Values)
  595. {
  596. pipeline?.Dispose();
  597. }
  598. }
  599. if (_computePipelineCache != null)
  600. {
  601. foreach (Auto<DisposablePipeline> pipeline in _computePipelineCache.Values)
  602. {
  603. pipeline.Dispose();
  604. }
  605. }
  606. for (int i = 0; i < Templates.Length; i++)
  607. {
  608. Templates[i]?.Dispose();
  609. }
  610. if (_dummyRenderPass.Value.Handle != 0)
  611. {
  612. _dummyRenderPass.Dispose();
  613. }
  614. }
  615. }
  616. public void Dispose()
  617. {
  618. Dispose(true);
  619. }
  620. }
  621. }