ShaderCollection.cs 24 KB

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