ShaderConfig.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Numerics;
  6. namespace Ryujinx.Graphics.Shader.Translation
  7. {
  8. class ShaderConfig
  9. {
  10. // TODO: Non-hardcoded array size.
  11. public const int SamplerArraySize = 4;
  12. public ShaderStage Stage { get; }
  13. public bool GpPassthrough { get; }
  14. public int ThreadsPerInputPrimitive { get; }
  15. public OutputTopology OutputTopology { get; }
  16. public int MaxOutputVertices { get; }
  17. public int LocalMemorySize { get; }
  18. public ImapPixelType[] ImapTypes { get; }
  19. public OmapTarget[] OmapTargets { get; }
  20. public bool OmapSampleMask { get; }
  21. public bool OmapDepth { get; }
  22. public IGpuAccessor GpuAccessor { get; }
  23. public TranslationOptions Options { get; }
  24. public int Size { get; private set; }
  25. public byte ClipDistancesWritten { get; private set; }
  26. public FeatureFlags UsedFeatures { get; private set; }
  27. public HashSet<int> TextureHandlesForCache { get; }
  28. private readonly TranslationCounts _counts;
  29. public int UsedInputAttributes { get; private set; }
  30. public int UsedInputAttributesPerPatch { get; private set; }
  31. public int UsedOutputAttributes { get; private set; }
  32. public int UsedOutputAttributesPerPatch { get; private set; }
  33. public int PassthroughAttributes { get; private set; }
  34. private int _usedConstantBuffers;
  35. private int _usedStorageBuffers;
  36. private int _usedStorageBuffersWrite;
  37. private struct TextureInfo : IEquatable<TextureInfo>
  38. {
  39. public int CbufSlot { get; }
  40. public int Handle { get; }
  41. public bool Indexed { get; }
  42. public TextureFormat Format { get; }
  43. public TextureInfo(int cbufSlot, int handle, bool indexed, TextureFormat format)
  44. {
  45. CbufSlot = cbufSlot;
  46. Handle = handle;
  47. Indexed = indexed;
  48. Format = format;
  49. }
  50. public override bool Equals(object obj)
  51. {
  52. return obj is TextureInfo other && Equals(other);
  53. }
  54. public bool Equals(TextureInfo other)
  55. {
  56. return CbufSlot == other.CbufSlot && Handle == other.Handle && Indexed == other.Indexed && Format == other.Format;
  57. }
  58. public override int GetHashCode()
  59. {
  60. return HashCode.Combine(CbufSlot, Handle, Indexed, Format);
  61. }
  62. }
  63. private struct TextureMeta
  64. {
  65. public bool AccurateType;
  66. public SamplerType Type;
  67. public TextureUsageFlags UsageFlags;
  68. }
  69. private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures;
  70. private readonly Dictionary<TextureInfo, TextureMeta> _usedImages;
  71. private BufferDescriptor[] _cachedConstantBufferDescriptors;
  72. private BufferDescriptor[] _cachedStorageBufferDescriptors;
  73. private TextureDescriptor[] _cachedTextureDescriptors;
  74. private TextureDescriptor[] _cachedImageDescriptors;
  75. public int FirstConstantBufferBinding { get; private set; }
  76. public int FirstStorageBufferBinding { get; private set; }
  77. public ShaderConfig(IGpuAccessor gpuAccessor, TranslationOptions options, TranslationCounts counts)
  78. {
  79. Stage = ShaderStage.Compute;
  80. GpuAccessor = gpuAccessor;
  81. Options = options;
  82. _counts = counts;
  83. TextureHandlesForCache = new HashSet<int>();
  84. _usedTextures = new Dictionary<TextureInfo, TextureMeta>();
  85. _usedImages = new Dictionary<TextureInfo, TextureMeta>();
  86. }
  87. public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options, TranslationCounts counts) : this(gpuAccessor, options, counts)
  88. {
  89. Stage = header.Stage;
  90. GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
  91. ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive;
  92. OutputTopology = header.OutputTopology;
  93. MaxOutputVertices = header.MaxOutputVertexCount;
  94. LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
  95. ImapTypes = header.ImapTypes;
  96. OmapTargets = header.OmapTargets;
  97. OmapSampleMask = header.OmapSampleMask;
  98. OmapDepth = header.OmapDepth;
  99. }
  100. public int GetDepthRegister()
  101. {
  102. int count = 0;
  103. for (int index = 0; index < OmapTargets.Length; index++)
  104. {
  105. for (int component = 0; component < 4; component++)
  106. {
  107. if (OmapTargets[index].ComponentEnabled(component))
  108. {
  109. count++;
  110. }
  111. }
  112. }
  113. // The depth register is always two registers after the last color output.
  114. return count + 1;
  115. }
  116. public TextureFormat GetTextureFormat(int handle, int cbufSlot = -1)
  117. {
  118. // When the formatted load extension is supported, we don't need to
  119. // specify a format, we can just declare it without a format and the GPU will handle it.
  120. if (GpuAccessor.QueryHostSupportsImageLoadFormatted())
  121. {
  122. return TextureFormat.Unknown;
  123. }
  124. var format = GpuAccessor.QueryTextureFormat(handle, cbufSlot);
  125. if (format == TextureFormat.Unknown)
  126. {
  127. GpuAccessor.Log($"Unknown format for texture {handle}.");
  128. format = TextureFormat.R8G8B8A8Unorm;
  129. }
  130. return format;
  131. }
  132. private bool FormatSupportsAtomic(TextureFormat format)
  133. {
  134. return format == TextureFormat.R32Sint || format == TextureFormat.R32Uint;
  135. }
  136. public TextureFormat GetTextureFormatAtomic(int handle, int cbufSlot = -1)
  137. {
  138. // Atomic image instructions do not support GL_EXT_shader_image_load_formatted,
  139. // and must have a type specified. Default to R32Sint if not available.
  140. var format = GpuAccessor.QueryTextureFormat(handle, cbufSlot);
  141. if (!FormatSupportsAtomic(format))
  142. {
  143. GpuAccessor.Log($"Unsupported format for texture {handle}: {format}.");
  144. format = TextureFormat.R32Sint;
  145. }
  146. return format;
  147. }
  148. public void SizeAdd(int size)
  149. {
  150. Size += size;
  151. }
  152. public void InheritFrom(ShaderConfig other)
  153. {
  154. ClipDistancesWritten |= other.ClipDistancesWritten;
  155. UsedFeatures |= other.UsedFeatures;
  156. TextureHandlesForCache.UnionWith(other.TextureHandlesForCache);
  157. UsedInputAttributes |= other.UsedInputAttributes;
  158. UsedOutputAttributes |= other.UsedOutputAttributes;
  159. _usedConstantBuffers |= other._usedConstantBuffers;
  160. _usedStorageBuffers |= other._usedStorageBuffers;
  161. _usedStorageBuffersWrite |= other._usedStorageBuffersWrite;
  162. foreach (var kv in other._usedTextures)
  163. {
  164. if (!_usedTextures.TryAdd(kv.Key, kv.Value))
  165. {
  166. _usedTextures[kv.Key] = MergeTextureMeta(kv.Value, _usedTextures[kv.Key]);
  167. }
  168. }
  169. foreach (var kv in other._usedImages)
  170. {
  171. if (!_usedImages.TryAdd(kv.Key, kv.Value))
  172. {
  173. _usedImages[kv.Key] = MergeTextureMeta(kv.Value, _usedImages[kv.Key]);
  174. }
  175. }
  176. }
  177. public void SetInputUserAttribute(int index, bool perPatch)
  178. {
  179. if (perPatch)
  180. {
  181. UsedInputAttributesPerPatch |= 1 << index;
  182. }
  183. else
  184. {
  185. UsedInputAttributes |= 1 << index;
  186. }
  187. }
  188. public void SetOutputUserAttribute(int index, bool perPatch)
  189. {
  190. if (perPatch)
  191. {
  192. UsedOutputAttributesPerPatch |= 1 << index;
  193. }
  194. else
  195. {
  196. UsedOutputAttributes |= 1 << index;
  197. }
  198. }
  199. public void MergeOutputUserAttributes(int mask, int maskPerPatch)
  200. {
  201. if (GpPassthrough)
  202. {
  203. PassthroughAttributes = mask & ~UsedOutputAttributes;
  204. }
  205. else
  206. {
  207. UsedOutputAttributes |= mask;
  208. UsedOutputAttributesPerPatch |= maskPerPatch;
  209. }
  210. }
  211. public void SetAllInputUserAttributes()
  212. {
  213. UsedInputAttributes |= Constants.AllAttributesMask;
  214. }
  215. public void SetAllOutputUserAttributes()
  216. {
  217. UsedOutputAttributes |= Constants.AllAttributesMask;
  218. }
  219. public void SetClipDistanceWritten(int index)
  220. {
  221. ClipDistancesWritten |= (byte)(1 << index);
  222. }
  223. public void SetUsedFeature(FeatureFlags flags)
  224. {
  225. UsedFeatures |= flags;
  226. }
  227. public Operand CreateCbuf(int slot, int offset)
  228. {
  229. SetUsedConstantBuffer(slot);
  230. return OperandHelper.Cbuf(slot, offset);
  231. }
  232. public void SetUsedConstantBuffer(int slot)
  233. {
  234. _usedConstantBuffers |= 1 << slot;
  235. }
  236. public void SetUsedStorageBuffer(int slot, bool write)
  237. {
  238. int mask = 1 << slot;
  239. _usedStorageBuffers |= mask;
  240. if (write)
  241. {
  242. _usedStorageBuffersWrite |= mask;
  243. }
  244. }
  245. public void SetUsedTexture(
  246. Instruction inst,
  247. SamplerType type,
  248. TextureFormat format,
  249. TextureFlags flags,
  250. int cbufSlot,
  251. int handle)
  252. {
  253. inst &= Instruction.Mask;
  254. bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
  255. bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
  256. bool accurateType = inst != Instruction.Lod;
  257. if (isImage)
  258. {
  259. SetUsedTextureOrImage(_usedImages, cbufSlot, handle, type, format, true, isWrite, false);
  260. }
  261. else
  262. {
  263. bool intCoords = flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureSize;
  264. SetUsedTextureOrImage(_usedTextures, cbufSlot, handle, type, TextureFormat.Unknown, intCoords, false, accurateType);
  265. }
  266. }
  267. private void SetUsedTextureOrImage(
  268. Dictionary<TextureInfo, TextureMeta> dict,
  269. int cbufSlot,
  270. int handle,
  271. SamplerType type,
  272. TextureFormat format,
  273. bool intCoords,
  274. bool write,
  275. bool accurateType)
  276. {
  277. var dimensions = type.GetDimensions();
  278. var isIndexed = type.HasFlag(SamplerType.Indexed);
  279. var usageFlags = TextureUsageFlags.None;
  280. if (intCoords)
  281. {
  282. usageFlags |= TextureUsageFlags.NeedsScaleValue;
  283. var canScale = (Stage == ShaderStage.Fragment || Stage == ShaderStage.Compute) && !isIndexed && !write && dimensions == 2;
  284. if (!canScale)
  285. {
  286. // Resolution scaling cannot be applied to this texture right now.
  287. // Flag so that we know to blacklist scaling on related textures when binding them.
  288. usageFlags |= TextureUsageFlags.ResScaleUnsupported;
  289. }
  290. }
  291. if (write)
  292. {
  293. usageFlags |= TextureUsageFlags.ImageStore;
  294. }
  295. int arraySize = isIndexed ? SamplerArraySize : 1;
  296. for (int layer = 0; layer < arraySize; layer++)
  297. {
  298. var info = new TextureInfo(cbufSlot, handle + layer * 2, isIndexed, format);
  299. var meta = new TextureMeta()
  300. {
  301. AccurateType = accurateType,
  302. Type = type,
  303. UsageFlags = usageFlags
  304. };
  305. if (dict.TryGetValue(info, out var existingMeta))
  306. {
  307. dict[info] = MergeTextureMeta(meta, existingMeta);
  308. }
  309. else
  310. {
  311. dict.Add(info, meta);
  312. }
  313. }
  314. }
  315. private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta)
  316. {
  317. meta.UsageFlags |= existingMeta.UsageFlags;
  318. // If the texture we have has inaccurate type information, then
  319. // we prefer the most accurate one.
  320. if (existingMeta.AccurateType)
  321. {
  322. meta.AccurateType = true;
  323. meta.Type = existingMeta.Type;
  324. }
  325. return meta;
  326. }
  327. public BufferDescriptor[] GetConstantBufferDescriptors()
  328. {
  329. if (_cachedConstantBufferDescriptors != null)
  330. {
  331. return _cachedConstantBufferDescriptors;
  332. }
  333. int usedMask = _usedConstantBuffers;
  334. if (UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
  335. {
  336. usedMask |= (int)GpuAccessor.QueryConstantBufferUse();
  337. }
  338. FirstConstantBufferBinding = _counts.UniformBuffersCount;
  339. return _cachedConstantBufferDescriptors = GetBufferDescriptors(
  340. usedMask,
  341. 0,
  342. UsedFeatures.HasFlag(FeatureFlags.CbIndexing),
  343. _counts.IncrementUniformBuffersCount);
  344. }
  345. public BufferDescriptor[] GetStorageBufferDescriptors()
  346. {
  347. if (_cachedStorageBufferDescriptors != null)
  348. {
  349. return _cachedStorageBufferDescriptors;
  350. }
  351. FirstStorageBufferBinding = _counts.StorageBuffersCount;
  352. return _cachedStorageBufferDescriptors = GetBufferDescriptors(
  353. _usedStorageBuffers,
  354. _usedStorageBuffersWrite,
  355. true,
  356. _counts.IncrementStorageBuffersCount);
  357. }
  358. private static BufferDescriptor[] GetBufferDescriptors(
  359. int usedMask,
  360. int writtenMask,
  361. bool isArray,
  362. Func<int> getBindingCallback)
  363. {
  364. var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)];
  365. int lastSlot = -1;
  366. for (int i = 0; i < descriptors.Length; i++)
  367. {
  368. int slot = BitOperations.TrailingZeroCount(usedMask);
  369. if (isArray)
  370. {
  371. // The next array entries also consumes bindings, even if they are unused.
  372. for (int j = lastSlot + 1; j < slot; j++)
  373. {
  374. getBindingCallback();
  375. }
  376. }
  377. lastSlot = slot;
  378. descriptors[i] = new BufferDescriptor(getBindingCallback(), slot);
  379. if ((writtenMask & (1 << slot)) != 0)
  380. {
  381. descriptors[i].SetFlag(BufferUsageFlags.Write);
  382. }
  383. usedMask &= ~(1 << slot);
  384. }
  385. return descriptors;
  386. }
  387. public TextureDescriptor[] GetTextureDescriptors()
  388. {
  389. return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, _counts.IncrementTexturesCount);
  390. }
  391. public TextureDescriptor[] GetImageDescriptors()
  392. {
  393. return _cachedImageDescriptors ??= GetTextureOrImageDescriptors(_usedImages, _counts.IncrementImagesCount);
  394. }
  395. private static TextureDescriptor[] GetTextureOrImageDescriptors(Dictionary<TextureInfo, TextureMeta> dict, Func<int> getBindingCallback)
  396. {
  397. var descriptors = new TextureDescriptor[dict.Count];
  398. int i = 0;
  399. foreach (var kv in dict.OrderBy(x => x.Key.Indexed).OrderBy(x => x.Key.Handle))
  400. {
  401. var info = kv.Key;
  402. var meta = kv.Value;
  403. int binding = getBindingCallback();
  404. descriptors[i] = new TextureDescriptor(binding, meta.Type, info.Format, info.CbufSlot, info.Handle);
  405. descriptors[i].SetFlag(meta.UsageFlags);
  406. i++;
  407. }
  408. return descriptors;
  409. }
  410. }
  411. }