SizeCalculator.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. using Ryujinx.Common;
  2. using System;
  3. using static Ryujinx.Graphics.Texture.BlockLinearConstants;
  4. namespace Ryujinx.Graphics.Texture
  5. {
  6. public static class SizeCalculator
  7. {
  8. private const int StrideAlignment = 32;
  9. private static int Calculate3DOffsetCount(int levels, int depth)
  10. {
  11. int offsetCount = depth;
  12. while (--levels > 0)
  13. {
  14. depth = Math.Max(1, depth >> 1);
  15. offsetCount += depth;
  16. }
  17. return offsetCount;
  18. }
  19. public static SizeInfo GetBlockLinearTextureSize(
  20. int width,
  21. int height,
  22. int depth,
  23. int levels,
  24. int layers,
  25. int blockWidth,
  26. int blockHeight,
  27. int bytesPerPixel,
  28. int gobBlocksInY,
  29. int gobBlocksInZ,
  30. int gobBlocksInTileX,
  31. int gpuLayerSize = 0)
  32. {
  33. bool is3D = depth > 1;
  34. int layerSize = 0;
  35. int[] allOffsets = new int[is3D ? Calculate3DOffsetCount(levels, depth) : levels * layers * depth];
  36. int[] mipOffsets = new int[levels];
  37. int[] sliceSizes = new int[levels];
  38. int mipGobBlocksInY = gobBlocksInY;
  39. int mipGobBlocksInZ = gobBlocksInZ;
  40. int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX;
  41. int gobHeight = gobBlocksInY * GobHeight;
  42. int depthLevelOffset = 0;
  43. for (int level = 0; level < levels; level++)
  44. {
  45. int w = Math.Max(1, width >> level);
  46. int h = Math.Max(1, height >> level);
  47. int d = Math.Max(1, depth >> level);
  48. w = BitUtils.DivRoundUp(w, blockWidth);
  49. h = BitUtils.DivRoundUp(h, blockHeight);
  50. while (h <= (mipGobBlocksInY >> 1) * GobHeight && mipGobBlocksInY != 1)
  51. {
  52. mipGobBlocksInY >>= 1;
  53. }
  54. while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1)
  55. {
  56. mipGobBlocksInZ >>= 1;
  57. }
  58. int widthInGobs = BitUtils.DivRoundUp(w * bytesPerPixel, GobStride);
  59. int alignment = gobBlocksInTileX;
  60. if (d < gobBlocksInZ || w <= gobWidth || h <= gobHeight)
  61. {
  62. alignment = 1;
  63. }
  64. widthInGobs = BitUtils.AlignUp(widthInGobs, alignment);
  65. int totalBlocksOfGobsInZ = BitUtils.DivRoundUp(d, mipGobBlocksInZ);
  66. int totalBlocksOfGobsInY = BitUtils.DivRoundUp(BitUtils.DivRoundUp(h, GobHeight), mipGobBlocksInY);
  67. int robSize = widthInGobs * mipGobBlocksInY * mipGobBlocksInZ * GobSize;
  68. if (is3D)
  69. {
  70. int gobSize = mipGobBlocksInY * GobSize;
  71. int sliceSize = totalBlocksOfGobsInY * widthInGobs * gobSize;
  72. int baseOffset = layerSize;
  73. int mask = gobBlocksInZ - 1;
  74. for (int z = 0; z < d; z++)
  75. {
  76. int zLow = z & mask;
  77. int zHigh = z & ~mask;
  78. allOffsets[z + depthLevelOffset] = baseOffset + zLow * gobSize + zHigh * sliceSize;
  79. }
  80. }
  81. mipOffsets[level] = layerSize;
  82. sliceSizes[level] = totalBlocksOfGobsInY * robSize;
  83. layerSize += totalBlocksOfGobsInZ * sliceSizes[level];
  84. depthLevelOffset += d;
  85. }
  86. if (layers > 1)
  87. {
  88. layerSize = AlignLayerSize(
  89. layerSize,
  90. height,
  91. depth,
  92. blockHeight,
  93. gobBlocksInY,
  94. gobBlocksInZ,
  95. gobBlocksInTileX);
  96. }
  97. int totalSize;
  98. if (layerSize < gpuLayerSize)
  99. {
  100. totalSize = (layers - 1) * gpuLayerSize + layerSize;
  101. layerSize = gpuLayerSize;
  102. }
  103. else
  104. {
  105. totalSize = layerSize * layers;
  106. }
  107. if (!is3D)
  108. {
  109. for (int layer = 0; layer < layers; layer++)
  110. {
  111. int baseIndex = layer * levels;
  112. int baseOffset = layer * layerSize;
  113. for (int level = 0; level < levels; level++)
  114. {
  115. allOffsets[baseIndex + level] = baseOffset + mipOffsets[level];
  116. }
  117. }
  118. }
  119. return new SizeInfo(mipOffsets, allOffsets, sliceSizes, depth, levels, layerSize, totalSize, is3D);
  120. }
  121. public static SizeInfo GetLinearTextureSize(int stride, int height, int blockHeight)
  122. {
  123. // Non-2D or mipmapped linear textures are not supported by the Switch GPU,
  124. // so we only need to handle a single case (2D textures without mipmaps).
  125. int totalSize = stride * BitUtils.DivRoundUp(height, blockHeight);
  126. return new SizeInfo(totalSize);
  127. }
  128. private static int AlignLayerSize(
  129. int size,
  130. int height,
  131. int depth,
  132. int blockHeight,
  133. int gobBlocksInY,
  134. int gobBlocksInZ,
  135. int gobBlocksInTileX)
  136. {
  137. if (gobBlocksInTileX < 2)
  138. {
  139. height = BitUtils.DivRoundUp(height, blockHeight);
  140. while (height <= (gobBlocksInY >> 1) * GobHeight && gobBlocksInY != 1)
  141. {
  142. gobBlocksInY >>= 1;
  143. }
  144. while (depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1)
  145. {
  146. gobBlocksInZ >>= 1;
  147. }
  148. int blockOfGobsSize = gobBlocksInY * gobBlocksInZ * GobSize;
  149. int sizeInBlockOfGobs = size / blockOfGobsSize;
  150. if (size != sizeInBlockOfGobs * blockOfGobsSize)
  151. {
  152. size = (sizeInBlockOfGobs + 1) * blockOfGobsSize;
  153. }
  154. }
  155. else
  156. {
  157. int alignment = (gobBlocksInTileX * GobSize) * gobBlocksInY * gobBlocksInZ;
  158. size = BitUtils.AlignUp(size, alignment);
  159. }
  160. return size;
  161. }
  162. public static Size GetBlockLinearAlignedSize(
  163. int width,
  164. int height,
  165. int depth,
  166. int blockWidth,
  167. int blockHeight,
  168. int bytesPerPixel,
  169. int gobBlocksInY,
  170. int gobBlocksInZ,
  171. int gobBlocksInTileX)
  172. {
  173. width = BitUtils.DivRoundUp(width, blockWidth);
  174. height = BitUtils.DivRoundUp(height, blockHeight);
  175. int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX;
  176. int gobHeight = gobBlocksInY * GobHeight;
  177. int alignment = gobWidth;
  178. if (depth < gobBlocksInZ || width <= gobWidth || height <= gobHeight)
  179. {
  180. alignment = GobStride / bytesPerPixel;
  181. }
  182. // Height has already been divided by block height, so pass it as 1.
  183. (gobBlocksInY, gobBlocksInZ) = GetMipGobBlockSizes(height, depth, 1, gobBlocksInY, gobBlocksInZ);
  184. int blockOfGobsHeight = gobBlocksInY * GobHeight;
  185. int blockOfGobsDepth = gobBlocksInZ;
  186. width = BitUtils.AlignUp(width, alignment);
  187. height = BitUtils.AlignUp(height, blockOfGobsHeight);
  188. depth = BitUtils.AlignUp(depth, blockOfGobsDepth);
  189. return new Size(width, height, depth);
  190. }
  191. public static Size GetLinearAlignedSize(
  192. int width,
  193. int height,
  194. int blockWidth,
  195. int blockHeight,
  196. int bytesPerPixel)
  197. {
  198. width = BitUtils.DivRoundUp(width, blockWidth);
  199. height = BitUtils.DivRoundUp(height, blockHeight);
  200. int widthAlignment = StrideAlignment / bytesPerPixel;
  201. width = BitUtils.AlignUp(width, widthAlignment);
  202. return new Size(width, height, 1);
  203. }
  204. public static (int, int) GetMipGobBlockSizes(
  205. int height,
  206. int depth,
  207. int blockHeight,
  208. int gobBlocksInY,
  209. int gobBlocksInZ)
  210. {
  211. height = BitUtils.DivRoundUp(height, blockHeight);
  212. while (height <= (gobBlocksInY >> 1) * GobHeight && gobBlocksInY != 1)
  213. {
  214. gobBlocksInY >>= 1;
  215. }
  216. while (depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1)
  217. {
  218. gobBlocksInZ >>= 1;
  219. }
  220. return (gobBlocksInY, gobBlocksInZ);
  221. }
  222. }
  223. }