InstGenMemory.cs 21 KB

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