InstGenMemory.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  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. // TODO: Bindless texture support. For now we just return 0/do nothing.
  15. if (isBindless)
  16. {
  17. return texOp.Inst == Instruction.ImageLoad ? NumberFormatter.FormatFloat(0) : "// imageStore(bindless)";
  18. }
  19. bool isArray = (texOp.Type & SamplerType.Array) != 0;
  20. bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
  21. string texCall = texOp.Inst == Instruction.ImageLoad ? "imageLoad" : "imageStore";
  22. int srcIndex = isBindless ? 1 : 0;
  23. string Src(VariableType type)
  24. {
  25. return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
  26. }
  27. string indexExpr = null;
  28. if (isIndexed)
  29. {
  30. indexExpr = Src(VariableType.S32);
  31. }
  32. string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr);
  33. texCall += "(" + imageName;
  34. int coordsCount = texOp.Type.GetDimensions();
  35. int pCount = coordsCount + (isArray ? 1 : 0);
  36. void Append(string str)
  37. {
  38. texCall += ", " + str;
  39. }
  40. string ApplyScaling(string vector)
  41. {
  42. if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) &&
  43. texOp.Inst == Instruction.ImageLoad &&
  44. !isBindless &&
  45. !isIndexed)
  46. {
  47. // Image scales start after texture ones.
  48. int scaleIndex = context.Config.GetTextureDescriptors().Length + context.FindImageDescriptorIndex(texOp);
  49. if (pCount == 3 && isArray)
  50. {
  51. // The array index is not scaled, just x and y.
  52. vector = "ivec3(Helper_TexelFetchScale((" + vector + ").xy, " + scaleIndex + "), (" + vector + ").z)";
  53. }
  54. else if (pCount == 2 && !isArray)
  55. {
  56. vector = "Helper_TexelFetchScale(" + vector + ", " + scaleIndex + ")";
  57. }
  58. }
  59. return vector;
  60. }
  61. if (pCount > 1)
  62. {
  63. string[] elems = new string[pCount];
  64. for (int index = 0; index < pCount; index++)
  65. {
  66. elems[index] = Src(VariableType.S32);
  67. }
  68. Append(ApplyScaling("ivec" + pCount + "(" + string.Join(", ", elems) + ")"));
  69. }
  70. else
  71. {
  72. Append(Src(VariableType.S32));
  73. }
  74. if (texOp.Inst == Instruction.ImageStore)
  75. {
  76. int texIndex = context.FindImageDescriptorIndex(texOp);
  77. VariableType type = texOp.Format.GetComponentType();
  78. string[] cElems = new string[4];
  79. for (int index = 0; index < 4; index++)
  80. {
  81. if (srcIndex < texOp.SourcesCount)
  82. {
  83. cElems[index] = Src(type);
  84. }
  85. else
  86. {
  87. cElems[index] = type switch
  88. {
  89. VariableType.S32 => NumberFormatter.FormatInt(0),
  90. VariableType.U32 => NumberFormatter.FormatUint(0),
  91. _ => NumberFormatter.FormatFloat(0)
  92. };
  93. }
  94. }
  95. string prefix = type switch
  96. {
  97. VariableType.S32 => "i",
  98. VariableType.U32 => "u",
  99. _ => string.Empty
  100. };
  101. Append(prefix + "vec4(" + string.Join(", ", cElems) + ")");
  102. }
  103. texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMask(texOp.Index) : "");
  104. return texCall;
  105. }
  106. public static string LoadAttribute(CodeGenContext context, AstOperation operation)
  107. {
  108. IAstNode src1 = operation.GetSource(0);
  109. IAstNode src2 = operation.GetSource(1);
  110. if (!(src1 is AstOperand attr) || attr.Type != OperandType.Attribute)
  111. {
  112. throw new InvalidOperationException("First source of LoadAttribute must be a attribute.");
  113. }
  114. string indexExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
  115. return OperandManager.GetAttributeName(attr, context.Config, isOutAttr: false, indexExpr);
  116. }
  117. public static string LoadConstant(CodeGenContext context, AstOperation operation)
  118. {
  119. IAstNode src1 = operation.GetSource(0);
  120. IAstNode src2 = operation.GetSource(1);
  121. string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
  122. offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true);
  123. var config = context.Config;
  124. bool indexElement = !config.GpuAccessor.QueryHostHasVectorIndexingBug();
  125. if (src1 is AstOperand oper && oper.Type == OperandType.Constant)
  126. {
  127. bool cbIndexable = config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing);
  128. return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, config.Stage, cbIndexable, indexElement);
  129. }
  130. else
  131. {
  132. string slotExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
  133. return OperandManager.GetConstantBufferName(slotExpr, offsetExpr, config.Stage, indexElement);
  134. }
  135. }
  136. public static string LoadLocal(CodeGenContext context, AstOperation operation)
  137. {
  138. return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
  139. }
  140. public static string LoadShared(CodeGenContext context, AstOperation operation)
  141. {
  142. return LoadLocalOrShared(context, operation, DefaultNames.SharedMemoryName);
  143. }
  144. private static string LoadLocalOrShared(CodeGenContext context, AstOperation operation, string arrayName)
  145. {
  146. IAstNode src1 = operation.GetSource(0);
  147. string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
  148. return $"{arrayName}[{offsetExpr}]";
  149. }
  150. public static string LoadStorage(CodeGenContext context, AstOperation operation)
  151. {
  152. IAstNode src1 = operation.GetSource(0);
  153. IAstNode src2 = operation.GetSource(1);
  154. string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
  155. string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
  156. return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
  157. }
  158. public static string Lod(CodeGenContext context, AstOperation operation)
  159. {
  160. AstTextureOperation texOp = (AstTextureOperation)operation;
  161. int coordsCount = texOp.Type.GetDimensions();
  162. bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
  163. // TODO: Bindless texture support. For now we just return 0.
  164. if (isBindless)
  165. {
  166. return NumberFormatter.FormatFloat(0);
  167. }
  168. bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
  169. string indexExpr = null;
  170. if (isIndexed)
  171. {
  172. indexExpr = GetSoureExpr(context, texOp.GetSource(0), VariableType.S32);
  173. }
  174. string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
  175. int coordsIndex = isBindless || isIndexed ? 1 : 0;
  176. string coordsExpr;
  177. if (coordsCount > 1)
  178. {
  179. string[] elems = new string[coordsCount];
  180. for (int index = 0; index < coordsCount; index++)
  181. {
  182. elems[index] = GetSoureExpr(context, texOp.GetSource(coordsIndex + index), VariableType.F32);
  183. }
  184. coordsExpr = "vec" + coordsCount + "(" + string.Join(", ", elems) + ")";
  185. }
  186. else
  187. {
  188. coordsExpr = GetSoureExpr(context, texOp.GetSource(coordsIndex), VariableType.F32);
  189. }
  190. return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}";
  191. }
  192. public static string StoreLocal(CodeGenContext context, AstOperation operation)
  193. {
  194. return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
  195. }
  196. public static string StoreShared(CodeGenContext context, AstOperation operation)
  197. {
  198. return StoreLocalOrShared(context, operation, DefaultNames.SharedMemoryName);
  199. }
  200. private static string StoreLocalOrShared(CodeGenContext context, AstOperation operation, string arrayName)
  201. {
  202. IAstNode src1 = operation.GetSource(0);
  203. IAstNode src2 = operation.GetSource(1);
  204. string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
  205. VariableType srcType = OperandManager.GetNodeDestType(context, src2);
  206. string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.U32);
  207. return $"{arrayName}[{offsetExpr}] = {src}";
  208. }
  209. public static string StoreStorage(CodeGenContext context, AstOperation operation)
  210. {
  211. IAstNode src1 = operation.GetSource(0);
  212. IAstNode src2 = operation.GetSource(1);
  213. IAstNode src3 = operation.GetSource(2);
  214. string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
  215. string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
  216. VariableType srcType = OperandManager.GetNodeDestType(context, src3);
  217. string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.U32);
  218. string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
  219. return $"{sb} = {src}";
  220. }
  221. public static string TextureSample(CodeGenContext context, AstOperation operation)
  222. {
  223. AstTextureOperation texOp = (AstTextureOperation)operation;
  224. bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
  225. bool isGather = (texOp.Flags & TextureFlags.Gather) != 0;
  226. bool hasDerivatives = (texOp.Flags & TextureFlags.Derivatives) != 0;
  227. bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
  228. bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0;
  229. bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0;
  230. bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
  231. bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
  232. bool isArray = (texOp.Type & SamplerType.Array) != 0;
  233. bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
  234. bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
  235. bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
  236. SamplerType type = texOp.Type & SamplerType.Mask;
  237. bool is2D = type == SamplerType.Texture2D;
  238. bool isCube = type == SamplerType.TextureCube;
  239. // 2D Array and Cube shadow samplers with LOD level or bias requires an extension.
  240. // If the extension is not supported, just remove the LOD parameter.
  241. if (isArray && isShadow && (is2D || isCube) && !context.Config.GpuAccessor.QueryHostSupportsTextureShadowLod())
  242. {
  243. hasLodBias = false;
  244. hasLodLevel = false;
  245. }
  246. // Cube shadow samplers with LOD level requires an extension.
  247. // If the extension is not supported, just remove the LOD level parameter.
  248. if (isShadow && isCube && !context.Config.GpuAccessor.QueryHostSupportsTextureShadowLod())
  249. {
  250. hasLodLevel = false;
  251. }
  252. // TODO: Bindless texture support. For now we just return 0.
  253. if (isBindless)
  254. {
  255. return NumberFormatter.FormatFloat(0);
  256. }
  257. string texCall = intCoords ? "texelFetch" : "texture";
  258. if (isGather)
  259. {
  260. texCall += "Gather";
  261. }
  262. else if (hasDerivatives)
  263. {
  264. texCall += "Grad";
  265. }
  266. else if (hasLodLevel && !intCoords)
  267. {
  268. texCall += "Lod";
  269. }
  270. if (hasOffset)
  271. {
  272. texCall += "Offset";
  273. }
  274. else if (hasOffsets)
  275. {
  276. texCall += "Offsets";
  277. }
  278. int srcIndex = isBindless ? 1 : 0;
  279. string Src(VariableType type)
  280. {
  281. return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
  282. }
  283. string indexExpr = null;
  284. if (isIndexed)
  285. {
  286. indexExpr = Src(VariableType.S32);
  287. }
  288. string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
  289. texCall += "(" + samplerName;
  290. int coordsCount = texOp.Type.GetDimensions();
  291. int pCount = coordsCount;
  292. int arrayIndexElem = -1;
  293. if (isArray)
  294. {
  295. arrayIndexElem = pCount++;
  296. }
  297. // The sampler 1D shadow overload expects a
  298. // dummy value on the middle of the vector, who knows why...
  299. bool hasDummy1DShadowElem = texOp.Type == (SamplerType.Texture1D | SamplerType.Shadow);
  300. if (hasDummy1DShadowElem)
  301. {
  302. pCount++;
  303. }
  304. if (isShadow && !isGather)
  305. {
  306. pCount++;
  307. }
  308. // On textureGather*, the comparison value is
  309. // always specified as an extra argument.
  310. bool hasExtraCompareArg = isShadow && isGather;
  311. if (pCount == 5)
  312. {
  313. pCount = 4;
  314. hasExtraCompareArg = true;
  315. }
  316. void Append(string str)
  317. {
  318. texCall += ", " + str;
  319. }
  320. VariableType coordType = intCoords ? VariableType.S32 : VariableType.F32;
  321. string AssemblePVector(int count)
  322. {
  323. if (count > 1)
  324. {
  325. string[] elems = new string[count];
  326. for (int index = 0; index < count; index++)
  327. {
  328. if (arrayIndexElem == index)
  329. {
  330. elems[index] = Src(VariableType.S32);
  331. if (!intCoords)
  332. {
  333. elems[index] = "float(" + elems[index] + ")";
  334. }
  335. }
  336. else if (index == 1 && hasDummy1DShadowElem)
  337. {
  338. elems[index] = NumberFormatter.FormatFloat(0);
  339. }
  340. else
  341. {
  342. elems[index] = Src(coordType);
  343. }
  344. }
  345. string prefix = intCoords ? "i" : string.Empty;
  346. return prefix + "vec" + count + "(" + string.Join(", ", elems) + ")";
  347. }
  348. else
  349. {
  350. return Src(coordType);
  351. }
  352. }
  353. string ApplyScaling(string vector)
  354. {
  355. if (intCoords)
  356. {
  357. if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) &&
  358. !isBindless &&
  359. !isIndexed)
  360. {
  361. int index = context.FindTextureDescriptorIndex(texOp);
  362. if (pCount == 3 && isArray)
  363. {
  364. // The array index is not scaled, just x and y.
  365. vector = "ivec3(Helper_TexelFetchScale((" + vector + ").xy, " + index + "), (" + vector + ").z)";
  366. }
  367. else if (pCount == 2 && !isArray)
  368. {
  369. vector = "Helper_TexelFetchScale(" + vector + ", " + index + ")";
  370. }
  371. }
  372. }
  373. return vector;
  374. }
  375. Append(ApplyScaling(AssemblePVector(pCount)));
  376. string AssembleDerivativesVector(int count)
  377. {
  378. if (count > 1)
  379. {
  380. string[] elems = new string[count];
  381. for (int index = 0; index < count; index++)
  382. {
  383. elems[index] = Src(VariableType.F32);
  384. }
  385. return "vec" + count + "(" + string.Join(", ", elems) + ")";
  386. }
  387. else
  388. {
  389. return Src(VariableType.F32);
  390. }
  391. }
  392. if (hasExtraCompareArg)
  393. {
  394. Append(Src(VariableType.F32));
  395. }
  396. if (hasDerivatives)
  397. {
  398. Append(AssembleDerivativesVector(coordsCount)); // dPdx
  399. Append(AssembleDerivativesVector(coordsCount)); // dPdy
  400. }
  401. if (isMultisample)
  402. {
  403. Append(Src(VariableType.S32));
  404. }
  405. else if (hasLodLevel)
  406. {
  407. Append(Src(coordType));
  408. }
  409. string AssembleOffsetVector(int count)
  410. {
  411. if (count > 1)
  412. {
  413. string[] elems = new string[count];
  414. for (int index = 0; index < count; index++)
  415. {
  416. elems[index] = Src(VariableType.S32);
  417. }
  418. return "ivec" + count + "(" + string.Join(", ", elems) + ")";
  419. }
  420. else
  421. {
  422. return Src(VariableType.S32);
  423. }
  424. }
  425. if (hasOffset)
  426. {
  427. Append(AssembleOffsetVector(coordsCount));
  428. }
  429. else if (hasOffsets)
  430. {
  431. texCall += $", ivec{coordsCount}[4](";
  432. texCall += AssembleOffsetVector(coordsCount) + ", ";
  433. texCall += AssembleOffsetVector(coordsCount) + ", ";
  434. texCall += AssembleOffsetVector(coordsCount) + ", ";
  435. texCall += AssembleOffsetVector(coordsCount) + ")";
  436. }
  437. if (hasLodBias)
  438. {
  439. Append(Src(VariableType.F32));
  440. }
  441. // textureGather* optional extra component index,
  442. // not needed for shadow samplers.
  443. if (isGather && !isShadow)
  444. {
  445. Append(Src(VariableType.S32));
  446. }
  447. texCall += ")" + (isGather || !isShadow ? GetMask(texOp.Index) : "");
  448. return texCall;
  449. }
  450. public static string TextureSize(CodeGenContext context, AstOperation operation)
  451. {
  452. AstTextureOperation texOp = (AstTextureOperation)operation;
  453. bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
  454. // TODO: Bindless texture support. For now we just return 0.
  455. if (isBindless)
  456. {
  457. return NumberFormatter.FormatInt(0);
  458. }
  459. bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
  460. string indexExpr = null;
  461. if (isIndexed)
  462. {
  463. indexExpr = GetSoureExpr(context, texOp.GetSource(0), VariableType.S32);
  464. }
  465. string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
  466. int lodSrcIndex = isBindless || isIndexed ? 1 : 0;
  467. IAstNode lod = operation.GetSource(lodSrcIndex);
  468. string lodExpr = GetSoureExpr(context, lod, GetSrcVarType(operation.Inst, lodSrcIndex));
  469. if (texOp.Index == 3)
  470. {
  471. return $"textureQueryLevels({samplerName})";
  472. }
  473. else
  474. {
  475. string texCall = $"textureSize({samplerName}, {lodExpr}){GetMask(texOp.Index)}";
  476. if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) &&
  477. !isBindless &&
  478. !isIndexed)
  479. {
  480. int index = context.FindTextureDescriptorIndex(texOp);
  481. texCall = "Helper_TextureSizeUnscale(" + texCall + ", " + index + ")";
  482. }
  483. return texCall;
  484. }
  485. }
  486. private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage)
  487. {
  488. string sbName = OperandManager.GetShaderStagePrefix(stage);
  489. sbName += "_" + DefaultNames.StorageNamePrefix;
  490. return $"{sbName}[{slotExpr}].{DefaultNames.DataName}[{offsetExpr}]";
  491. }
  492. private static string GetMask(int index)
  493. {
  494. return '.' + "rgba".Substring(index, 1);
  495. }
  496. }
  497. }