InstGenMemory.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  2. using Ryujinx.Graphics.Shader.StructuredIr;
  3. using System;
  4. using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
  5. using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
  6. namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
  7. {
  8. static class InstGenMemory
  9. {
  10. public static string ImageLoadOrStore(CodeGenContext context, AstOperation operation)
  11. {
  12. AstTextureOperation texOp = (AstTextureOperation)operation;
  13. bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
  14. bool isArray = (texOp.Type & SamplerType.Array) != 0;
  15. bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
  16. string texCall = texOp.Inst == Instruction.ImageLoad ? "imageLoad" : "imageStore";
  17. int srcIndex = isBindless ? 1 : 0;
  18. string Src(VariableType type)
  19. {
  20. return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
  21. }
  22. string indexExpr = null;
  23. if (isIndexed)
  24. {
  25. indexExpr = Src(VariableType.S32);
  26. }
  27. string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr);
  28. texCall += "(" + imageName;
  29. int coordsCount = texOp.Type.GetDimensions();
  30. int pCount = coordsCount + (isArray ? 1 : 0);
  31. void Append(string str)
  32. {
  33. texCall += ", " + str;
  34. }
  35. if (pCount > 1)
  36. {
  37. string[] elems = new string[pCount];
  38. for (int index = 0; index < pCount; index++)
  39. {
  40. elems[index] = Src(VariableType.S32);
  41. }
  42. Append("ivec" + pCount + "(" + string.Join(", ", elems) + ")");
  43. }
  44. else
  45. {
  46. Append(Src(VariableType.S32));
  47. }
  48. if (texOp.Inst == Instruction.ImageStore)
  49. {
  50. VariableType type = texOp.Format.GetComponentType();
  51. string[] cElems = new string[4];
  52. for (int index = 0; index < 4; index++)
  53. {
  54. if (srcIndex < texOp.SourcesCount)
  55. {
  56. cElems[index] = Src(type);
  57. }
  58. else
  59. {
  60. cElems[index] = type switch
  61. {
  62. VariableType.S32 => NumberFormatter.FormatInt(0),
  63. VariableType.U32 => NumberFormatter.FormatUint(0),
  64. _ => NumberFormatter.FormatFloat(0)
  65. };
  66. }
  67. }
  68. string prefix = type switch
  69. {
  70. VariableType.S32 => "i",
  71. VariableType.U32 => "u",
  72. _ => string.Empty
  73. };
  74. Append(prefix + "vec4(" + string.Join(", ", cElems) + ")");
  75. }
  76. texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMask(texOp.Index) : "");
  77. return texCall;
  78. }
  79. public static string LoadAttribute(CodeGenContext context, AstOperation operation)
  80. {
  81. IAstNode src1 = operation.GetSource(0);
  82. IAstNode src2 = operation.GetSource(1);
  83. if (!(src1 is AstOperand attr) || attr.Type != OperandType.Attribute)
  84. {
  85. throw new InvalidOperationException("First source of LoadAttribute must be a attribute.");
  86. }
  87. string indexExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
  88. return OperandManager.GetAttributeName(attr, context.Config, isOutAttr: false, indexExpr);
  89. }
  90. public static string LoadConstant(CodeGenContext context, AstOperation operation)
  91. {
  92. IAstNode src1 = operation.GetSource(0);
  93. IAstNode src2 = operation.GetSource(1);
  94. string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
  95. offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true);
  96. if (src1 is AstOperand oper && oper.Type == OperandType.Constant)
  97. {
  98. return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, context.Config.Stage, context.CbIndexable);
  99. }
  100. else
  101. {
  102. string slotExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
  103. return OperandManager.GetConstantBufferName(slotExpr, offsetExpr, context.Config.Stage);
  104. }
  105. }
  106. public static string LoadLocal(CodeGenContext context, AstOperation operation)
  107. {
  108. return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
  109. }
  110. public static string LoadShared(CodeGenContext context, AstOperation operation)
  111. {
  112. return LoadLocalOrShared(context, operation, DefaultNames.SharedMemoryName);
  113. }
  114. private static string LoadLocalOrShared(CodeGenContext context, AstOperation operation, string arrayName)
  115. {
  116. IAstNode src1 = operation.GetSource(0);
  117. string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
  118. return $"{arrayName}[{offsetExpr}]";
  119. }
  120. public static string LoadStorage(CodeGenContext context, AstOperation operation)
  121. {
  122. IAstNode src1 = operation.GetSource(0);
  123. IAstNode src2 = operation.GetSource(1);
  124. string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
  125. string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
  126. return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
  127. }
  128. public static string Lod(CodeGenContext context, AstOperation operation)
  129. {
  130. AstTextureOperation texOp = (AstTextureOperation)operation;
  131. int coordsCount = texOp.Type.GetDimensions();
  132. bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
  133. bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
  134. string indexExpr = null;
  135. if (isIndexed)
  136. {
  137. indexExpr = GetSoureExpr(context, texOp.GetSource(0), VariableType.S32);
  138. }
  139. string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
  140. int coordsIndex = isBindless || isIndexed ? 1 : 0;
  141. string coordsExpr;
  142. if (coordsCount > 1)
  143. {
  144. string[] elems = new string[coordsCount];
  145. for (int index = 0; index < coordsCount; index++)
  146. {
  147. elems[index] = GetSoureExpr(context, texOp.GetSource(coordsIndex + index), VariableType.F32);
  148. }
  149. coordsExpr = "vec" + coordsCount + "(" + string.Join(", ", elems) + ")";
  150. }
  151. else
  152. {
  153. coordsExpr = GetSoureExpr(context, texOp.GetSource(coordsIndex), VariableType.F32);
  154. }
  155. return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}";
  156. }
  157. public static string StoreLocal(CodeGenContext context, AstOperation operation)
  158. {
  159. return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
  160. }
  161. public static string StoreShared(CodeGenContext context, AstOperation operation)
  162. {
  163. return StoreLocalOrShared(context, operation, DefaultNames.SharedMemoryName);
  164. }
  165. private static string StoreLocalOrShared(CodeGenContext context, AstOperation operation, string arrayName)
  166. {
  167. IAstNode src1 = operation.GetSource(0);
  168. IAstNode src2 = operation.GetSource(1);
  169. string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
  170. VariableType srcType = OperandManager.GetNodeDestType(context, src2);
  171. string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.U32);
  172. return $"{arrayName}[{offsetExpr}] = {src}";
  173. }
  174. public static string StoreStorage(CodeGenContext context, AstOperation operation)
  175. {
  176. IAstNode src1 = operation.GetSource(0);
  177. IAstNode src2 = operation.GetSource(1);
  178. IAstNode src3 = operation.GetSource(2);
  179. string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
  180. string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
  181. VariableType srcType = OperandManager.GetNodeDestType(context, src3);
  182. string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.U32);
  183. string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
  184. return $"{sb} = {src}";
  185. }
  186. public static string TextureSample(CodeGenContext context, AstOperation operation)
  187. {
  188. AstTextureOperation texOp = (AstTextureOperation)operation;
  189. bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
  190. bool isGather = (texOp.Flags & TextureFlags.Gather) != 0;
  191. bool hasDerivatives = (texOp.Flags & TextureFlags.Derivatives) != 0;
  192. bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
  193. bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0;
  194. bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0;
  195. bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
  196. bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
  197. bool isArray = (texOp.Type & SamplerType.Array) != 0;
  198. bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
  199. bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
  200. bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
  201. // This combination is valid, but not available on GLSL.
  202. // For now, ignore the LOD level and do a normal sample.
  203. // TODO: How to implement it properly?
  204. if (hasLodLevel && isArray && isShadow)
  205. {
  206. hasLodLevel = false;
  207. }
  208. string texCall = intCoords ? "texelFetch" : "texture";
  209. if (isGather)
  210. {
  211. texCall += "Gather";
  212. }
  213. else if (hasDerivatives)
  214. {
  215. texCall += "Grad";
  216. }
  217. else if (hasLodLevel && !intCoords)
  218. {
  219. texCall += "Lod";
  220. }
  221. if (hasOffset)
  222. {
  223. texCall += "Offset";
  224. }
  225. else if (hasOffsets)
  226. {
  227. texCall += "Offsets";
  228. }
  229. int srcIndex = isBindless ? 1 : 0;
  230. string Src(VariableType type)
  231. {
  232. return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
  233. }
  234. string indexExpr = null;
  235. if (isIndexed)
  236. {
  237. indexExpr = Src(VariableType.S32);
  238. }
  239. string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
  240. texCall += "(" + samplerName;
  241. int coordsCount = texOp.Type.GetDimensions();
  242. int pCount = coordsCount;
  243. int arrayIndexElem = -1;
  244. if (isArray)
  245. {
  246. arrayIndexElem = pCount++;
  247. }
  248. // The sampler 1D shadow overload expects a
  249. // dummy value on the middle of the vector, who knows why...
  250. bool hasDummy1DShadowElem = texOp.Type == (SamplerType.Texture1D | SamplerType.Shadow);
  251. if (hasDummy1DShadowElem)
  252. {
  253. pCount++;
  254. }
  255. if (isShadow && !isGather)
  256. {
  257. pCount++;
  258. }
  259. // On textureGather*, the comparison value is
  260. // always specified as an extra argument.
  261. bool hasExtraCompareArg = isShadow && isGather;
  262. if (pCount == 5)
  263. {
  264. pCount = 4;
  265. hasExtraCompareArg = true;
  266. }
  267. void Append(string str)
  268. {
  269. texCall += ", " + str;
  270. }
  271. VariableType coordType = intCoords ? VariableType.S32 : VariableType.F32;
  272. string AssemblePVector(int count)
  273. {
  274. if (count > 1)
  275. {
  276. string[] elems = new string[count];
  277. for (int index = 0; index < count; index++)
  278. {
  279. if (arrayIndexElem == index)
  280. {
  281. elems[index] = Src(VariableType.S32);
  282. if (!intCoords)
  283. {
  284. elems[index] = "float(" + elems[index] + ")";
  285. }
  286. }
  287. else if (index == 1 && hasDummy1DShadowElem)
  288. {
  289. elems[index] = NumberFormatter.FormatFloat(0);
  290. }
  291. else
  292. {
  293. elems[index] = Src(coordType);
  294. }
  295. }
  296. string prefix = intCoords ? "i" : string.Empty;
  297. return prefix + "vec" + count + "(" + string.Join(", ", elems) + ")";
  298. }
  299. else
  300. {
  301. return Src(coordType);
  302. }
  303. }
  304. string ApplyScaling(string vector)
  305. {
  306. if (intCoords)
  307. {
  308. int index = context.FindTextureDescriptorIndex(texOp);
  309. if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) &&
  310. (texOp.Flags & TextureFlags.Bindless) == 0 &&
  311. texOp.Type != SamplerType.Indexed &&
  312. pCount == 2)
  313. {
  314. return "Helper_TexelFetchScale(" + vector + ", " + index + ")";
  315. }
  316. else
  317. {
  318. // Resolution scaling cannot be applied to this texture right now.
  319. // Flag so that we know to blacklist scaling on related textures when binding them.
  320. TextureDescriptor descriptor = context.TextureDescriptors[index];
  321. descriptor.Flags |= TextureUsageFlags.ResScaleUnsupported;
  322. context.TextureDescriptors[index] = descriptor;
  323. }
  324. }
  325. return vector;
  326. }
  327. Append(ApplyScaling(AssemblePVector(pCount)));
  328. string AssembleDerivativesVector(int count)
  329. {
  330. if (count > 1)
  331. {
  332. string[] elems = new string[count];
  333. for (int index = 0; index < count; index++)
  334. {
  335. elems[index] = Src(VariableType.F32);
  336. }
  337. return "vec" + count + "(" + string.Join(", ", elems) + ")";
  338. }
  339. else
  340. {
  341. return Src(VariableType.F32);
  342. }
  343. }
  344. if (hasExtraCompareArg)
  345. {
  346. Append(Src(VariableType.F32));
  347. }
  348. if (hasDerivatives)
  349. {
  350. Append(AssembleDerivativesVector(coordsCount)); // dPdx
  351. Append(AssembleDerivativesVector(coordsCount)); // dPdy
  352. }
  353. if (isMultisample)
  354. {
  355. Append(Src(VariableType.S32));
  356. }
  357. else if (hasLodLevel)
  358. {
  359. Append(Src(coordType));
  360. }
  361. string AssembleOffsetVector(int count)
  362. {
  363. if (count > 1)
  364. {
  365. string[] elems = new string[count];
  366. for (int index = 0; index < count; index++)
  367. {
  368. elems[index] = Src(VariableType.S32);
  369. }
  370. return "ivec" + count + "(" + string.Join(", ", elems) + ")";
  371. }
  372. else
  373. {
  374. return Src(VariableType.S32);
  375. }
  376. }
  377. if (hasOffset)
  378. {
  379. Append(AssembleOffsetVector(coordsCount));
  380. }
  381. else if (hasOffsets)
  382. {
  383. texCall += $", ivec{coordsCount}[4](";
  384. texCall += AssembleOffsetVector(coordsCount) + ", ";
  385. texCall += AssembleOffsetVector(coordsCount) + ", ";
  386. texCall += AssembleOffsetVector(coordsCount) + ", ";
  387. texCall += AssembleOffsetVector(coordsCount) + ")";
  388. }
  389. if (hasLodBias)
  390. {
  391. Append(Src(VariableType.F32));
  392. }
  393. // textureGather* optional extra component index,
  394. // not needed for shadow samplers.
  395. if (isGather && !isShadow)
  396. {
  397. Append(Src(VariableType.S32));
  398. }
  399. texCall += ")" + (isGather || !isShadow ? GetMask(texOp.Index) : "");
  400. return texCall;
  401. }
  402. public static string TextureSize(CodeGenContext context, AstOperation operation)
  403. {
  404. AstTextureOperation texOp = (AstTextureOperation)operation;
  405. bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
  406. bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
  407. string indexExpr = null;
  408. if (isIndexed)
  409. {
  410. indexExpr = GetSoureExpr(context, texOp.GetSource(0), VariableType.S32);
  411. }
  412. string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
  413. int lodSrcIndex = isBindless || isIndexed ? 1 : 0;
  414. IAstNode lod = operation.GetSource(lodSrcIndex);
  415. string lodExpr = GetSoureExpr(context, lod, GetSrcVarType(operation.Inst, lodSrcIndex));
  416. if (texOp.Index == 3)
  417. {
  418. return $"textureQueryLevels({samplerName})";
  419. }
  420. else
  421. {
  422. return $"textureSize({samplerName}, {lodExpr}){GetMask(texOp.Index)}";
  423. }
  424. }
  425. private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage)
  426. {
  427. string sbName = OperandManager.GetShaderStagePrefix(stage);
  428. sbName += "_" + DefaultNames.StorageNamePrefix;
  429. return $"{sbName}[{slotExpr}].{DefaultNames.DataName}[{offsetExpr}]";
  430. }
  431. private static string GetMask(int index)
  432. {
  433. return '.' + "rgba".Substring(index, 1);
  434. }
  435. }
  436. }