TextureCompatibility.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. using Ryujinx.Common;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Graphics.Gpu.State;
  4. using Ryujinx.Graphics.Texture;
  5. using System;
  6. namespace Ryujinx.Graphics.Gpu.Image
  7. {
  8. /// <summary>
  9. /// Texture format compatibility checks.
  10. /// </summary>
  11. static class TextureCompatibility
  12. {
  13. private enum FormatClass
  14. {
  15. Unclassified,
  16. BCn64,
  17. BCn128,
  18. Bc1Rgb,
  19. Bc1Rgba,
  20. Bc2,
  21. Bc3,
  22. Bc4,
  23. Bc5,
  24. Bc6,
  25. Bc7
  26. }
  27. /// <summary>
  28. /// Finds the appropriate depth format for a copy texture if the source texture has a depth format.
  29. /// </summary>
  30. /// <param name="dstTextureFormat">Destination CopyTexture Format</param>
  31. /// <param name="srcTextureFormat">Source Texture Format</param>
  32. /// <returns>Derived RtFormat if srcTextureFormat is a depth format, otherwise return dstTextureFormat.</returns>
  33. public static RtFormat DeriveDepthFormat(RtFormat dstTextureFormat, Format srcTextureFormat)
  34. {
  35. return srcTextureFormat switch
  36. {
  37. Format.S8Uint => RtFormat.S8Uint,
  38. Format.D16Unorm => RtFormat.D16Unorm,
  39. Format.D24X8Unorm => RtFormat.D24Unorm,
  40. Format.D32Float => RtFormat.D32Float,
  41. Format.D24UnormS8Uint => RtFormat.D24UnormS8Uint,
  42. Format.D32FloatS8Uint => RtFormat.D32FloatS8Uint,
  43. _ => dstTextureFormat
  44. };
  45. }
  46. /// <summary>
  47. /// Checks if two formats are compatible, according to the host API copy format compatibility rules.
  48. /// </summary>
  49. /// <param name="lhs">First comparand</param>
  50. /// <param name="rhs">Second comparand</param>
  51. /// <returns>True if the formats are compatible, false otherwise</returns>
  52. public static bool FormatCompatible(FormatInfo lhs, FormatInfo rhs)
  53. {
  54. if (IsDsFormat(lhs.Format) || IsDsFormat(rhs.Format))
  55. {
  56. return lhs.Format == rhs.Format;
  57. }
  58. if (lhs.Format.IsAstc() || rhs.Format.IsAstc())
  59. {
  60. return lhs.Format == rhs.Format;
  61. }
  62. if (lhs.IsCompressed && rhs.IsCompressed)
  63. {
  64. FormatClass lhsClass = GetFormatClass(lhs.Format);
  65. FormatClass rhsClass = GetFormatClass(rhs.Format);
  66. return lhsClass == rhsClass;
  67. }
  68. else
  69. {
  70. return lhs.BytesPerPixel == rhs.BytesPerPixel;
  71. }
  72. }
  73. /// <summary>
  74. /// Checks if the texture format matches with the specified texture information.
  75. /// </summary>
  76. /// <param name="lhs">Texture information to compare</param>
  77. /// <param name="rhs">Texture information to compare with</param>
  78. /// <param name="forSampler">Indicates that the texture will be used for shader sampling</param>
  79. /// <param name="forCopy">Indicates that the texture will be used as copy source or target</param>
  80. /// <returns>True if the format matches, with the given comparison rules</returns>
  81. public static bool FormatMatches(TextureInfo lhs, TextureInfo rhs, bool forSampler, bool forCopy)
  82. {
  83. // D32F and R32F texture have the same representation internally,
  84. // however the R32F format is used to sample from depth textures.
  85. if (lhs.FormatInfo.Format == Format.D32Float && rhs.FormatInfo.Format == Format.R32Float && (forSampler || forCopy))
  86. {
  87. return true;
  88. }
  89. if (forCopy)
  90. {
  91. // The 2D engine does not support depth-stencil formats, so it will instead
  92. // use equivalent color formats. We must also consider them as compatible.
  93. if (lhs.FormatInfo.Format == Format.S8Uint && rhs.FormatInfo.Format == Format.R8Unorm)
  94. {
  95. return true;
  96. }
  97. if (lhs.FormatInfo.Format == Format.D16Unorm && rhs.FormatInfo.Format == Format.R16Unorm)
  98. {
  99. return true;
  100. }
  101. if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
  102. lhs.FormatInfo.Format == Format.D24X8Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
  103. {
  104. return true;
  105. }
  106. }
  107. return lhs.FormatInfo.Format == rhs.FormatInfo.Format;
  108. }
  109. /// <summary>
  110. /// Checks if the texture layout specified matches with this texture layout.
  111. /// The layout information is composed of the Stride for linear textures, or GOB block size
  112. /// for block linear textures.
  113. /// </summary>
  114. /// <param name="lhs">Texture information to compare</param>
  115. /// <param name="rhs">Texture information to compare with</param>
  116. /// <returns>True if the layout matches, false otherwise</returns>
  117. public static bool LayoutMatches(TextureInfo lhs, TextureInfo rhs)
  118. {
  119. if (lhs.IsLinear != rhs.IsLinear)
  120. {
  121. return false;
  122. }
  123. // For linear textures, gob block sizes are ignored.
  124. // For block linear textures, the stride is ignored.
  125. if (rhs.IsLinear)
  126. {
  127. return lhs.Stride == rhs.Stride;
  128. }
  129. else
  130. {
  131. return lhs.GobBlocksInY == rhs.GobBlocksInY &&
  132. lhs.GobBlocksInZ == rhs.GobBlocksInZ;
  133. }
  134. }
  135. /// <summary>
  136. /// Checks if the view sizes of a two given texture informations match.
  137. /// </summary>
  138. /// <param name="lhs">Texture information of the texture view</param>
  139. /// <param name="rhs">Texture information of the texture view to match against</param>
  140. /// <param name="level">Mipmap level of the texture view in relation to this texture</param>
  141. /// <param name="isCopy">True to check for copy compatibility rather than view compatibility</param>
  142. /// <returns>True if the sizes are compatible, false otherwise</returns>
  143. public static bool ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, int level, bool isCopy)
  144. {
  145. Size size = GetAlignedSize(lhs, level);
  146. Size otherSize = GetAlignedSize(rhs);
  147. // For copies, we can copy a subset of the 3D texture slices,
  148. // so the depth may be different in this case.
  149. if (!isCopy && rhs.Target == Target.Texture3D && size.Depth != otherSize.Depth)
  150. {
  151. return false;
  152. }
  153. return size.Width == otherSize.Width &&
  154. size.Height == otherSize.Height;
  155. }
  156. /// <summary>
  157. /// Checks if the texture sizes of the supplied texture informations match.
  158. /// </summary>
  159. /// <param name="lhs">Texture information to compare</param>
  160. /// <param name="rhs">Texture information to compare with</param>
  161. /// <returns>True if the size matches, false otherwise</returns>
  162. public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs)
  163. {
  164. return SizeMatches(lhs, rhs, alignSizes: false);
  165. }
  166. /// <summary>
  167. /// Checks if the texture sizes of the supplied texture informations match the given level
  168. /// </summary>
  169. /// <param name="lhs">Texture information to compare</param>
  170. /// <param name="rhs">Texture information to compare with</param>
  171. /// <param name="level">Mipmap level of this texture to compare with</param>
  172. /// <returns>True if the size matches with the level, false otherwise</returns>
  173. public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, int level)
  174. {
  175. return Math.Max(1, lhs.Width >> level) == rhs.Width &&
  176. Math.Max(1, lhs.Height >> level) == rhs.Height &&
  177. Math.Max(1, lhs.GetDepth() >> level) == rhs.GetDepth();
  178. }
  179. /// <summary>
  180. /// Checks if the texture sizes of the supplied texture informations match.
  181. /// </summary>
  182. /// <param name="lhs">Texture information to compare</param>
  183. /// <param name="rhs">Texture information to compare with</param>
  184. /// <param name="alignSizes">True to align the sizes according to the texture layout for comparison</param>
  185. /// <returns>True if the sizes matches, false otherwise</returns>
  186. public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool alignSizes)
  187. {
  188. if (lhs.GetLayers() != rhs.GetLayers())
  189. {
  190. return false;
  191. }
  192. if (alignSizes)
  193. {
  194. Size size0 = GetAlignedSize(lhs);
  195. Size size1 = GetAlignedSize(rhs);
  196. return size0.Width == size1.Width &&
  197. size0.Height == size1.Height &&
  198. size0.Depth == size1.Depth;
  199. }
  200. else
  201. {
  202. return lhs.Width == rhs.Width &&
  203. lhs.Height == rhs.Height &&
  204. lhs.GetDepth() == rhs.GetDepth();
  205. }
  206. }
  207. /// <summary>
  208. /// Gets the aligned sizes of the specified texture information.
  209. /// The alignment depends on the texture layout and format bytes per pixel.
  210. /// </summary>
  211. /// <param name="info">Texture information to calculate the aligned size from</param>
  212. /// <param name="level">Mipmap level for texture views</param>
  213. /// <returns>The aligned texture size</returns>
  214. public static Size GetAlignedSize(TextureInfo info, int level = 0)
  215. {
  216. int width = Math.Max(1, info.Width >> level);
  217. int height = Math.Max(1, info.Height >> level);
  218. if (info.IsLinear)
  219. {
  220. return SizeCalculator.GetLinearAlignedSize(
  221. width,
  222. height,
  223. info.FormatInfo.BlockWidth,
  224. info.FormatInfo.BlockHeight,
  225. info.FormatInfo.BytesPerPixel);
  226. }
  227. else
  228. {
  229. int depth = Math.Max(1, info.GetDepth() >> level);
  230. return SizeCalculator.GetBlockLinearAlignedSize(
  231. width,
  232. height,
  233. depth,
  234. info.FormatInfo.BlockWidth,
  235. info.FormatInfo.BlockHeight,
  236. info.FormatInfo.BytesPerPixel,
  237. info.GobBlocksInY,
  238. info.GobBlocksInZ,
  239. info.GobBlocksInTileX);
  240. }
  241. }
  242. /// <summary>
  243. /// Check if it's possible to create a view with the layout of the second texture information from the first.
  244. /// The layout information is composed of the Stride for linear textures, or GOB block size
  245. /// for block linear textures.
  246. /// </summary>
  247. /// <param name="lhs">Texture information of the texture view</param>
  248. /// <param name="rhs">Texture information of the texture view to compare against</param>
  249. /// <param name="level">Start level of the texture view, in relation with the first texture</param>
  250. /// <returns>True if the layout is compatible, false otherwise</returns>
  251. public static bool ViewLayoutCompatible(TextureInfo lhs, TextureInfo rhs, int level)
  252. {
  253. if (lhs.IsLinear != rhs.IsLinear)
  254. {
  255. return false;
  256. }
  257. // For linear textures, gob block sizes are ignored.
  258. // For block linear textures, the stride is ignored.
  259. if (rhs.IsLinear)
  260. {
  261. int width = Math.Max(1, lhs.Width >> level);
  262. int stride = width * lhs.FormatInfo.BytesPerPixel;
  263. stride = BitUtils.AlignUp(stride, 32);
  264. return stride == rhs.Stride;
  265. }
  266. else
  267. {
  268. int height = Math.Max(1, lhs.Height >> level);
  269. int depth = Math.Max(1, lhs.GetDepth() >> level);
  270. (int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
  271. height,
  272. depth,
  273. lhs.FormatInfo.BlockHeight,
  274. lhs.GobBlocksInY,
  275. lhs.GobBlocksInZ);
  276. return gobBlocksInY == rhs.GobBlocksInY &&
  277. gobBlocksInZ == rhs.GobBlocksInZ;
  278. }
  279. }
  280. /// <summary>
  281. /// Checks if the view format of the first texture format is compatible with the format of the second.
  282. /// In general, the formats are considered compatible if the bytes per pixel values are equal,
  283. /// but there are more complex rules for some formats, like compressed or depth-stencil formats.
  284. /// This follows the host API copy compatibility rules.
  285. /// </summary>
  286. /// <param name="lhs">Texture information of the texture view</param>
  287. /// <param name="rhs">Texture information of the texture view</param>
  288. /// <returns>True if the formats are compatible, false otherwise</returns>
  289. public static bool ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs)
  290. {
  291. return FormatCompatible(lhs.FormatInfo, rhs.FormatInfo);
  292. }
  293. /// <summary>
  294. /// Check if the target of the first texture view information is compatible with the target of the second texture view information.
  295. /// This follows the host API target compatibility rules.
  296. /// </summary>
  297. /// <param name="lhs">Texture information of the texture view</param
  298. /// <param name="rhs">Texture information of the texture view</param>
  299. /// <param name="isCopy">True to check for copy rather than view compatibility</param>
  300. /// <returns>True if the targets are compatible, false otherwise</returns>
  301. public static bool ViewTargetCompatible(TextureInfo lhs, TextureInfo rhs, bool isCopy)
  302. {
  303. switch (lhs.Target)
  304. {
  305. case Target.Texture1D:
  306. case Target.Texture1DArray:
  307. return rhs.Target == Target.Texture1D ||
  308. rhs.Target == Target.Texture1DArray;
  309. case Target.Texture2D:
  310. return rhs.Target == Target.Texture2D ||
  311. rhs.Target == Target.Texture2DArray;
  312. case Target.Texture2DArray:
  313. case Target.Cubemap:
  314. case Target.CubemapArray:
  315. return rhs.Target == Target.Texture2D ||
  316. rhs.Target == Target.Texture2DArray ||
  317. rhs.Target == Target.Cubemap ||
  318. rhs.Target == Target.CubemapArray;
  319. case Target.Texture2DMultisample:
  320. case Target.Texture2DMultisampleArray:
  321. return rhs.Target == Target.Texture2DMultisample ||
  322. rhs.Target == Target.Texture2DMultisampleArray;
  323. case Target.Texture3D:
  324. return rhs.Target == Target.Texture3D ||
  325. (rhs.Target == Target.Texture2D && isCopy);
  326. }
  327. return false;
  328. }
  329. /// <summary>
  330. /// Checks if the texture shader sampling parameters of two texture informations match.
  331. /// </summary>
  332. /// <param name="lhs">Texture information to compare</param>
  333. /// <param name="rhs">Texture information to compare with</param>
  334. /// <returns>True if the texture shader sampling parameters matches, false otherwise</returns>
  335. public static bool SamplerParamsMatches(TextureInfo lhs, TextureInfo rhs)
  336. {
  337. return lhs.DepthStencilMode == rhs.DepthStencilMode &&
  338. lhs.SwizzleR == rhs.SwizzleR &&
  339. lhs.SwizzleG == rhs.SwizzleG &&
  340. lhs.SwizzleB == rhs.SwizzleB &&
  341. lhs.SwizzleA == rhs.SwizzleA;
  342. }
  343. /// <summary>
  344. /// Check if the texture target and samples count (for multisampled textures) matches.
  345. /// </summary>
  346. /// <param name="first">Texture information to compare with</param>
  347. /// <param name="rhs">Texture information to compare with</param>
  348. /// <returns>True if the texture target and samples count matches, false otherwise</returns>
  349. public static bool TargetAndSamplesCompatible(TextureInfo lhs, TextureInfo rhs)
  350. {
  351. return lhs.Target == rhs.Target &&
  352. lhs.SamplesInX == rhs.SamplesInX &&
  353. lhs.SamplesInY == rhs.SamplesInY;
  354. }
  355. /// <summary>
  356. /// Gets the texture format class, for compressed textures, or Unclassified otherwise.
  357. /// </summary>
  358. /// <param name="format">The format</param>
  359. /// <returns>Format class</returns>
  360. private static FormatClass GetFormatClass(Format format)
  361. {
  362. switch (format)
  363. {
  364. case Format.Bc1RgbSrgb:
  365. case Format.Bc1RgbUnorm:
  366. return FormatClass.Bc1Rgb;
  367. case Format.Bc1RgbaSrgb:
  368. case Format.Bc1RgbaUnorm:
  369. return FormatClass.Bc1Rgba;
  370. case Format.Bc2Srgb:
  371. case Format.Bc2Unorm:
  372. return FormatClass.Bc2;
  373. case Format.Bc3Srgb:
  374. case Format.Bc3Unorm:
  375. return FormatClass.Bc3;
  376. case Format.Bc4Snorm:
  377. case Format.Bc4Unorm:
  378. return FormatClass.Bc4;
  379. case Format.Bc5Snorm:
  380. case Format.Bc5Unorm:
  381. return FormatClass.Bc5;
  382. case Format.Bc6HSfloat:
  383. case Format.Bc6HUfloat:
  384. return FormatClass.Bc6;
  385. case Format.Bc7Srgb:
  386. case Format.Bc7Unorm:
  387. return FormatClass.Bc7;
  388. }
  389. return FormatClass.Unclassified;
  390. }
  391. /// <summary>
  392. /// Checks if the format is a depth-stencil texture format.
  393. /// </summary>
  394. /// <param name="format">Format to check</param>
  395. /// <returns>True if the format is a depth-stencil format (including depth only), false otherwise</returns>
  396. private static bool IsDsFormat(Format format)
  397. {
  398. switch (format)
  399. {
  400. case Format.D16Unorm:
  401. case Format.D24X8Unorm:
  402. case Format.D24UnormS8Uint:
  403. case Format.D32Float:
  404. case Format.D32FloatS8Uint:
  405. case Format.S8Uint:
  406. return true;
  407. }
  408. return false;
  409. }
  410. }
  411. }