ImageUtils.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. using ChocolArm64.Memory;
  2. using Ryujinx.Graphics.Gal;
  3. using Ryujinx.Graphics.Memory;
  4. using System;
  5. using System.Collections.Generic;
  6. namespace Ryujinx.Graphics.Texture
  7. {
  8. public static class ImageUtils
  9. {
  10. [Flags]
  11. private enum TargetBuffer
  12. {
  13. Color = 1 << 0,
  14. Depth = 1 << 1,
  15. Stencil = 1 << 2,
  16. DepthStencil = Depth | Stencil
  17. }
  18. private struct ImageDescriptor
  19. {
  20. public int BytesPerPixel { get; private set; }
  21. public int BlockWidth { get; private set; }
  22. public int BlockHeight { get; private set; }
  23. public TargetBuffer Target { get; private set; }
  24. public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, TargetBuffer Target)
  25. {
  26. this.BytesPerPixel = BytesPerPixel;
  27. this.BlockWidth = BlockWidth;
  28. this.BlockHeight = BlockHeight;
  29. this.Target = Target;
  30. }
  31. }
  32. private const GalImageFormat Snorm = GalImageFormat.Snorm;
  33. private const GalImageFormat Unorm = GalImageFormat.Unorm;
  34. private const GalImageFormat Sint = GalImageFormat.Sint;
  35. private const GalImageFormat Uint = GalImageFormat.Uint;
  36. private const GalImageFormat Float = GalImageFormat.Float;
  37. private const GalImageFormat Srgb = GalImageFormat.Srgb;
  38. private static readonly Dictionary<GalTextureFormat, GalImageFormat> s_TextureTable =
  39. new Dictionary<GalTextureFormat, GalImageFormat>()
  40. {
  41. { GalTextureFormat.RGBA32, GalImageFormat.RGBA32 | Sint | Uint | Float },
  42. { GalTextureFormat.RGBA16, GalImageFormat.RGBA16 | Snorm | Unorm | Sint | Uint | Float },
  43. { GalTextureFormat.RG32, GalImageFormat.RG32 | Sint | Uint | Float },
  44. { GalTextureFormat.RGBA8, GalImageFormat.RGBA8 | Snorm | Unorm | Sint | Uint | Srgb },
  45. { GalTextureFormat.RGB10A2, GalImageFormat.RGB10A2 | Snorm | Unorm | Sint | Uint },
  46. { GalTextureFormat.RG8, GalImageFormat.RG8 | Snorm | Unorm | Sint | Uint },
  47. { GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Float },
  48. { GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint },
  49. { GalTextureFormat.RG16, GalImageFormat.RG16 | Snorm | Unorm | Float },
  50. { GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Float },
  51. { GalTextureFormat.RGBA4, GalImageFormat.RGBA4 | Unorm },
  52. { GalTextureFormat.RGB5A1, GalImageFormat.RGB5A1 | Unorm },
  53. { GalTextureFormat.RGB565, GalImageFormat.RGB565 | Unorm },
  54. { GalTextureFormat.R11G11B10F, GalImageFormat.R11G11B10 | Float },
  55. { GalTextureFormat.D24S8, GalImageFormat.D24S8 | Unorm | Uint },
  56. { GalTextureFormat.D32F, GalImageFormat.D32 | Float },
  57. { GalTextureFormat.D32FX24S8, GalImageFormat.D32S8 | Float },
  58. { GalTextureFormat.D16, GalImageFormat.D16 | Unorm },
  59. //Compressed formats
  60. { GalTextureFormat.BptcSfloat, GalImageFormat.BptcSfloat | Float },
  61. { GalTextureFormat.BptcUfloat, GalImageFormat.BptcUfloat | Float },
  62. { GalTextureFormat.BptcUnorm, GalImageFormat.BptcUnorm | Unorm | Srgb },
  63. { GalTextureFormat.BC1, GalImageFormat.BC1 | Unorm | Srgb },
  64. { GalTextureFormat.BC2, GalImageFormat.BC2 | Unorm | Srgb },
  65. { GalTextureFormat.BC3, GalImageFormat.BC3 | Unorm | Srgb },
  66. { GalTextureFormat.BC4, GalImageFormat.BC4 | Unorm | Snorm },
  67. { GalTextureFormat.BC5, GalImageFormat.BC5 | Unorm | Snorm },
  68. { GalTextureFormat.Astc2D4x4, GalImageFormat.Astc2D4x4 | Unorm | Srgb },
  69. { GalTextureFormat.Astc2D5x5, GalImageFormat.Astc2D5x5 | Unorm | Srgb },
  70. { GalTextureFormat.Astc2D6x6, GalImageFormat.Astc2D6x6 | Unorm | Srgb },
  71. { GalTextureFormat.Astc2D8x8, GalImageFormat.Astc2D8x8 | Unorm | Srgb },
  72. { GalTextureFormat.Astc2D10x10, GalImageFormat.Astc2D10x10 | Unorm | Srgb },
  73. { GalTextureFormat.Astc2D12x12, GalImageFormat.Astc2D12x12 | Unorm | Srgb },
  74. { GalTextureFormat.Astc2D5x4, GalImageFormat.Astc2D5x4 | Unorm | Srgb },
  75. { GalTextureFormat.Astc2D6x5, GalImageFormat.Astc2D6x5 | Unorm | Srgb },
  76. { GalTextureFormat.Astc2D8x6, GalImageFormat.Astc2D8x6 | Unorm | Srgb },
  77. { GalTextureFormat.Astc2D10x8, GalImageFormat.Astc2D10x8 | Unorm | Srgb },
  78. { GalTextureFormat.Astc2D12x10, GalImageFormat.Astc2D12x10 | Unorm | Srgb },
  79. { GalTextureFormat.Astc2D8x5, GalImageFormat.Astc2D8x5 | Unorm | Srgb },
  80. { GalTextureFormat.Astc2D10x5, GalImageFormat.Astc2D10x5 | Unorm | Srgb },
  81. { GalTextureFormat.Astc2D10x6, GalImageFormat.Astc2D10x6 | Unorm | Srgb }
  82. };
  83. private static readonly Dictionary<GalImageFormat, ImageDescriptor> s_ImageTable =
  84. new Dictionary<GalImageFormat, ImageDescriptor>()
  85. {
  86. { GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) },
  87. { GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) },
  88. { GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) },
  89. { GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
  90. { GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
  91. { GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
  92. { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
  93. { GalImageFormat.RGBA4, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
  94. { GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
  95. { GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
  96. { GalImageFormat.BGR5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
  97. { GalImageFormat.RGB5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
  98. { GalImageFormat.RGB565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
  99. { GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
  100. { GalImageFormat.RG16, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
  101. { GalImageFormat.RG8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
  102. { GalImageFormat.R16, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
  103. { GalImageFormat.R8, new ImageDescriptor(1, 1, 1, TargetBuffer.Color) },
  104. { GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
  105. { GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) },
  106. { GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
  107. { GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
  108. { GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) },
  109. { GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
  110. { GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
  111. { GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, TargetBuffer.Color) },
  112. { GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, TargetBuffer.Color) },
  113. { GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, TargetBuffer.Color) },
  114. { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) },
  115. { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) },
  116. { GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, TargetBuffer.Color) },
  117. { GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, TargetBuffer.Color) },
  118. { GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, TargetBuffer.Color) },
  119. { GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, TargetBuffer.Color) },
  120. { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) },
  121. { GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, TargetBuffer.Color) },
  122. { GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) },
  123. { GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) },
  124. { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) },
  125. { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) },
  126. { GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) },
  127. { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) }
  128. };
  129. public static GalImageFormat ConvertTexture(
  130. GalTextureFormat Format,
  131. GalTextureType RType,
  132. GalTextureType GType,
  133. GalTextureType BType,
  134. GalTextureType AType,
  135. bool ConvSrgb)
  136. {
  137. if (RType != GType || RType != BType || RType != AType)
  138. {
  139. throw new NotImplementedException("Per component types are not implemented!");
  140. }
  141. if (!s_TextureTable.TryGetValue(Format, out GalImageFormat ImageFormat))
  142. {
  143. throw new NotImplementedException($"Format 0x{((int)Format):x} not implemented!");
  144. }
  145. GalImageFormat FormatType = ConvSrgb ? Srgb : GetFormatType(RType);
  146. GalImageFormat CombinedFormat = (ImageFormat & GalImageFormat.FormatMask) | FormatType;
  147. if (!ImageFormat.HasFlag(FormatType))
  148. {
  149. throw new NotImplementedException($"Format \"{CombinedFormat}\" not implemented!");
  150. }
  151. return CombinedFormat;
  152. }
  153. public static GalImageFormat ConvertSurface(GalSurfaceFormat Format)
  154. {
  155. switch (Format)
  156. {
  157. case GalSurfaceFormat.RGBA32Float: return GalImageFormat.RGBA32 | Float;
  158. case GalSurfaceFormat.RGBA32Uint: return GalImageFormat.RGBA32 | Uint;
  159. case GalSurfaceFormat.RGBA16Float: return GalImageFormat.RGBA16 | Float;
  160. case GalSurfaceFormat.RG32Float: return GalImageFormat.RG32 | Float;
  161. case GalSurfaceFormat.RG32Sint: return GalImageFormat.RG32 | Sint;
  162. case GalSurfaceFormat.RG32Uint: return GalImageFormat.RG32 | Uint;
  163. case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.BGRA8 | Unorm;
  164. case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.BGRA8 | Srgb;
  165. case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.RGB10A2 | Unorm;
  166. case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.RGBA8 | Unorm;
  167. case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.RGBA8 | Srgb;
  168. case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.RGBA8 | Snorm;
  169. case GalSurfaceFormat.RG16Snorm: return GalImageFormat.RG16 | Snorm;
  170. case GalSurfaceFormat.RG16Unorm: return GalImageFormat.RG16 | Unorm;
  171. case GalSurfaceFormat.RG16Float: return GalImageFormat.RG16 | Float;
  172. case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.R11G11B10 | Float;
  173. case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Float;
  174. case GalSurfaceFormat.R32Uint: return GalImageFormat.R32 | Uint;
  175. case GalSurfaceFormat.RG8Unorm: return GalImageFormat.RG8 | Unorm;
  176. case GalSurfaceFormat.RG8Snorm: return GalImageFormat.RG8 | Snorm;
  177. case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Float;
  178. case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm;
  179. case GalSurfaceFormat.R16Uint: return GalImageFormat.R16 | Uint;
  180. case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm;
  181. case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint;
  182. case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.RGB565 | Unorm;
  183. case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.BGR5A1 | Unorm;
  184. }
  185. throw new NotImplementedException(Format.ToString());
  186. }
  187. public static GalImageFormat ConvertZeta(GalZetaFormat Format)
  188. {
  189. switch (Format)
  190. {
  191. case GalZetaFormat.D32Float: return GalImageFormat.D32 | Float;
  192. case GalZetaFormat.S8D24Unorm: return GalImageFormat.D24S8 | Unorm;
  193. case GalZetaFormat.D16Unorm: return GalImageFormat.D16 | Unorm;
  194. case GalZetaFormat.D24S8Unorm: return GalImageFormat.D24S8 | Unorm;
  195. case GalZetaFormat.D32S8X24Float: return GalImageFormat.D32S8 | Float;
  196. }
  197. throw new NotImplementedException(Format.ToString());
  198. }
  199. public static byte[] ReadTexture(IMemory Memory, GalImage Image, long Position)
  200. {
  201. MemoryManager CpuMemory;
  202. if (Memory is NvGpuVmm Vmm)
  203. {
  204. CpuMemory = Vmm.Memory;
  205. }
  206. else
  207. {
  208. CpuMemory = (MemoryManager)Memory;
  209. }
  210. ISwizzle Swizzle = TextureHelper.GetSwizzle(Image);
  211. ImageDescriptor Desc = GetImageDescriptor(Image.Format);
  212. (int Width, int Height) = GetImageSizeInBlocks(Image);
  213. int BytesPerPixel = Desc.BytesPerPixel;
  214. //Note: Each row of the texture needs to be aligned to 4 bytes.
  215. int Pitch = (Width * BytesPerPixel + 3) & ~3;
  216. byte[] Data = new byte[Height * Pitch];
  217. for (int Y = 0; Y < Height; Y++)
  218. {
  219. int OutOffs = Y * Pitch;
  220. for (int X = 0; X < Width; X++)
  221. {
  222. long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
  223. CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel);
  224. OutOffs += BytesPerPixel;
  225. }
  226. }
  227. return Data;
  228. }
  229. public static void WriteTexture(NvGpuVmm Vmm, GalImage Image, long Position, byte[] Data)
  230. {
  231. ISwizzle Swizzle = TextureHelper.GetSwizzle(Image);
  232. ImageDescriptor Desc = GetImageDescriptor(Image.Format);
  233. (int Width, int Height) = ImageUtils.GetImageSizeInBlocks(Image);
  234. int BytesPerPixel = Desc.BytesPerPixel;
  235. int InOffs = 0;
  236. for (int Y = 0; Y < Height; Y++)
  237. for (int X = 0; X < Width; X++)
  238. {
  239. long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
  240. Vmm.Memory.WriteBytes(Position + Offset, Data, InOffs, BytesPerPixel);
  241. InOffs += BytesPerPixel;
  242. }
  243. }
  244. public static int GetSize(GalImage Image)
  245. {
  246. ImageDescriptor Desc = GetImageDescriptor(Image.Format);
  247. int Width = DivRoundUp(Image.Width, Desc.BlockWidth);
  248. int Height = DivRoundUp(Image.Height, Desc.BlockHeight);
  249. return Desc.BytesPerPixel * Width * Height;
  250. }
  251. public static int GetPitch(GalImageFormat Format, int Width)
  252. {
  253. ImageDescriptor Desc = GetImageDescriptor(Format);
  254. int Pitch = Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth);
  255. Pitch = (Pitch + 0x1f) & ~0x1f;
  256. return Pitch;
  257. }
  258. public static int GetBlockWidth(GalImageFormat Format)
  259. {
  260. return GetImageDescriptor(Format).BlockWidth;
  261. }
  262. public static int GetBlockHeight(GalImageFormat Format)
  263. {
  264. return GetImageDescriptor(Format).BlockHeight;
  265. }
  266. public static int GetAlignedWidth(GalImage Image)
  267. {
  268. ImageDescriptor Desc = GetImageDescriptor(Image.Format);
  269. int AlignMask;
  270. if (Image.Layout == GalMemoryLayout.BlockLinear)
  271. {
  272. AlignMask = Image.TileWidth * (64 / Desc.BytesPerPixel) - 1;
  273. }
  274. else
  275. {
  276. AlignMask = (32 / Desc.BytesPerPixel) - 1;
  277. }
  278. return (Image.Width + AlignMask) & ~AlignMask;
  279. }
  280. public static (int Width, int Height) GetImageSizeInBlocks(GalImage Image)
  281. {
  282. ImageDescriptor Desc = GetImageDescriptor(Image.Format);
  283. return (DivRoundUp(Image.Width, Desc.BlockWidth),
  284. DivRoundUp(Image.Height, Desc.BlockHeight));
  285. }
  286. public static int GetBytesPerPixel(GalImageFormat Format)
  287. {
  288. return GetImageDescriptor(Format).BytesPerPixel;
  289. }
  290. private static int DivRoundUp(int LHS, int RHS)
  291. {
  292. return (LHS + (RHS - 1)) / RHS;
  293. }
  294. public static bool HasColor(GalImageFormat Format)
  295. {
  296. return (GetImageDescriptor(Format).Target & TargetBuffer.Color) != 0;
  297. }
  298. public static bool HasDepth(GalImageFormat Format)
  299. {
  300. return (GetImageDescriptor(Format).Target & TargetBuffer.Depth) != 0;
  301. }
  302. public static bool HasStencil(GalImageFormat Format)
  303. {
  304. return (GetImageDescriptor(Format).Target & TargetBuffer.Stencil) != 0;
  305. }
  306. public static bool IsCompressed(GalImageFormat Format)
  307. {
  308. ImageDescriptor Desc = GetImageDescriptor(Format);
  309. return (Desc.BlockWidth | Desc.BlockHeight) != 1;
  310. }
  311. private static ImageDescriptor GetImageDescriptor(GalImageFormat Format)
  312. {
  313. GalImageFormat PixelFormat = Format & GalImageFormat.FormatMask;
  314. if (s_ImageTable.TryGetValue(PixelFormat, out ImageDescriptor Descriptor))
  315. {
  316. return Descriptor;
  317. }
  318. throw new NotImplementedException($"Format \"{PixelFormat}\" not implemented!");
  319. }
  320. private static GalImageFormat GetFormatType(GalTextureType Type)
  321. {
  322. switch (Type)
  323. {
  324. case GalTextureType.Snorm: return Snorm;
  325. case GalTextureType.Unorm: return Unorm;
  326. case GalTextureType.Sint: return Sint;
  327. case GalTextureType.Uint: return Uint;
  328. case GalTextureType.Float: return Float;
  329. default: throw new NotImplementedException(((int)Type).ToString());
  330. }
  331. }
  332. }
  333. }