OglTexture.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. using OpenTK.Graphics.OpenGL;
  2. using Ryujinx.Graphics.Texture;
  3. using System;
  4. namespace Ryujinx.Graphics.Gal.OpenGL
  5. {
  6. class OglTexture : IGalTexture
  7. {
  8. private const long MaxTextureCacheSize = 768 * 1024 * 1024;
  9. private OglCachedResource<ImageHandler> _textureCache;
  10. public EventHandler<int> TextureDeleted { get; set; }
  11. public OglTexture()
  12. {
  13. _textureCache = new OglCachedResource<ImageHandler>(DeleteTexture, MaxTextureCacheSize);
  14. }
  15. public void LockCache()
  16. {
  17. _textureCache.Lock();
  18. }
  19. public void UnlockCache()
  20. {
  21. _textureCache.Unlock();
  22. }
  23. private void DeleteTexture(ImageHandler cachedImage)
  24. {
  25. TextureDeleted?.Invoke(this, cachedImage.Handle);
  26. GL.DeleteTexture(cachedImage.Handle);
  27. }
  28. public void Create(long key, int size, GalImage image)
  29. {
  30. int handle = GL.GenTexture();
  31. TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
  32. GL.BindTexture(target, handle);
  33. const int level = 0; //TODO: Support mipmap textures.
  34. const int border = 0;
  35. _textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)size);
  36. if (ImageUtils.IsCompressed(image.Format))
  37. {
  38. throw new InvalidOperationException("Surfaces with compressed formats are not supported!");
  39. }
  40. (PixelInternalFormat internalFmt,
  41. PixelFormat format,
  42. PixelType type) = OglEnumConverter.GetImageFormat(image.Format);
  43. switch (target)
  44. {
  45. case TextureTarget.Texture1D:
  46. GL.TexImage1D(
  47. target,
  48. level,
  49. internalFmt,
  50. image.Width,
  51. border,
  52. format,
  53. type,
  54. IntPtr.Zero);
  55. break;
  56. case TextureTarget.Texture2D:
  57. GL.TexImage2D(
  58. target,
  59. level,
  60. internalFmt,
  61. image.Width,
  62. image.Height,
  63. border,
  64. format,
  65. type,
  66. IntPtr.Zero);
  67. break;
  68. case TextureTarget.Texture3D:
  69. GL.TexImage3D(
  70. target,
  71. level,
  72. internalFmt,
  73. image.Width,
  74. image.Height,
  75. image.Depth,
  76. border,
  77. format,
  78. type,
  79. IntPtr.Zero);
  80. break;
  81. // Cube map arrays are just 2D texture arrays with 6 entries
  82. // per cube map so we can handle them in the same way
  83. case TextureTarget.TextureCubeMapArray:
  84. case TextureTarget.Texture2DArray:
  85. GL.TexImage3D(
  86. target,
  87. level,
  88. internalFmt,
  89. image.Width,
  90. image.Height,
  91. image.LayerCount,
  92. border,
  93. format,
  94. type,
  95. IntPtr.Zero);
  96. break;
  97. default:
  98. throw new NotImplementedException($"Unsupported texture target type: {target}");
  99. }
  100. }
  101. public void Create(long key, byte[] data, GalImage image)
  102. {
  103. int handle = GL.GenTexture();
  104. TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
  105. GL.BindTexture(target, handle);
  106. const int level = 0; //TODO: Support mipmap textures.
  107. const int border = 0;
  108. _textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)data.Length);
  109. if (ImageUtils.IsCompressed(image.Format) && !IsAstc(image.Format))
  110. {
  111. InternalFormat internalFmt = OglEnumConverter.GetCompressedImageFormat(image.Format);
  112. switch (target)
  113. {
  114. case TextureTarget.Texture1D:
  115. GL.CompressedTexImage1D(
  116. target,
  117. level,
  118. internalFmt,
  119. image.Width,
  120. border,
  121. data.Length,
  122. data);
  123. break;
  124. case TextureTarget.Texture2D:
  125. GL.CompressedTexImage2D(
  126. target,
  127. level,
  128. internalFmt,
  129. image.Width,
  130. image.Height,
  131. border,
  132. data.Length,
  133. data);
  134. break;
  135. case TextureTarget.Texture3D:
  136. GL.CompressedTexImage3D(
  137. target,
  138. level,
  139. internalFmt,
  140. image.Width,
  141. image.Height,
  142. image.Depth,
  143. border,
  144. data.Length,
  145. data);
  146. break;
  147. // Cube map arrays are just 2D texture arrays with 6 entries
  148. // per cube map so we can handle them in the same way
  149. case TextureTarget.TextureCubeMapArray:
  150. case TextureTarget.Texture2DArray:
  151. GL.CompressedTexImage3D(
  152. target,
  153. level,
  154. internalFmt,
  155. image.Width,
  156. image.Height,
  157. image.LayerCount,
  158. border,
  159. data.Length,
  160. data);
  161. break;
  162. case TextureTarget.TextureCubeMap:
  163. Span<byte> array = new Span<byte>(data);
  164. int faceSize = ImageUtils.GetSize(image) / 6;
  165. for (int Face = 0; Face < 6; Face++)
  166. {
  167. GL.CompressedTexImage2D(
  168. TextureTarget.TextureCubeMapPositiveX + Face,
  169. level,
  170. internalFmt,
  171. image.Width,
  172. image.Height,
  173. border,
  174. faceSize,
  175. array.Slice(Face * faceSize, faceSize).ToArray());
  176. }
  177. break;
  178. default:
  179. throw new NotImplementedException($"Unsupported texture target type: {target}");
  180. }
  181. }
  182. else
  183. {
  184. // TODO: Use KHR_texture_compression_astc_hdr when available
  185. if (IsAstc(image.Format))
  186. {
  187. int textureBlockWidth = ImageUtils.GetBlockWidth(image.Format);
  188. int textureBlockHeight = ImageUtils.GetBlockHeight(image.Format);
  189. int textureBlockDepth = ImageUtils.GetBlockDepth(image.Format);
  190. data = AstcDecoder.DecodeToRgba8888(
  191. data,
  192. textureBlockWidth,
  193. textureBlockHeight,
  194. textureBlockDepth,
  195. image.Width,
  196. image.Height,
  197. image.Depth);
  198. image.Format = GalImageFormat.Rgba8 | (image.Format & GalImageFormat.TypeMask);
  199. }
  200. (PixelInternalFormat internalFmt,
  201. PixelFormat format,
  202. PixelType type) = OglEnumConverter.GetImageFormat(image.Format);
  203. switch (target)
  204. {
  205. case TextureTarget.Texture1D:
  206. GL.TexImage1D(
  207. target,
  208. level,
  209. internalFmt,
  210. image.Width,
  211. border,
  212. format,
  213. type,
  214. data);
  215. break;
  216. case TextureTarget.Texture2D:
  217. GL.TexImage2D(
  218. target,
  219. level,
  220. internalFmt,
  221. image.Width,
  222. image.Height,
  223. border,
  224. format,
  225. type,
  226. data);
  227. break;
  228. case TextureTarget.Texture3D:
  229. GL.TexImage3D(
  230. target,
  231. level,
  232. internalFmt,
  233. image.Width,
  234. image.Height,
  235. image.Depth,
  236. border,
  237. format,
  238. type,
  239. data);
  240. break;
  241. // Cube map arrays are just 2D texture arrays with 6 entries
  242. // per cube map so we can handle them in the same way
  243. case TextureTarget.TextureCubeMapArray:
  244. case TextureTarget.Texture2DArray:
  245. GL.TexImage3D(
  246. target,
  247. level,
  248. internalFmt,
  249. image.Width,
  250. image.Height,
  251. image.LayerCount,
  252. border,
  253. format,
  254. type,
  255. data);
  256. break;
  257. case TextureTarget.TextureCubeMap:
  258. Span<byte> array = new Span<byte>(data);
  259. int faceSize = ImageUtils.GetSize(image) / 6;
  260. for (int face = 0; face < 6; face++)
  261. {
  262. GL.TexImage2D(
  263. TextureTarget.TextureCubeMapPositiveX + face,
  264. level,
  265. internalFmt,
  266. image.Width,
  267. image.Height,
  268. border,
  269. format,
  270. type,
  271. array.Slice(face * faceSize, faceSize).ToArray());
  272. }
  273. break;
  274. default:
  275. throw new NotImplementedException($"Unsupported texture target type: {target}");
  276. }
  277. }
  278. }
  279. private static bool IsAstc(GalImageFormat format)
  280. {
  281. format &= GalImageFormat.FormatMask;
  282. return format > GalImageFormat.Astc2DStart && format < GalImageFormat.Astc2DEnd;
  283. }
  284. public bool TryGetImage(long key, out GalImage image)
  285. {
  286. if (_textureCache.TryGetValue(key, out ImageHandler cachedImage))
  287. {
  288. image = cachedImage.Image;
  289. return true;
  290. }
  291. image = default(GalImage);
  292. return false;
  293. }
  294. public bool TryGetImageHandler(long key, out ImageHandler cachedImage)
  295. {
  296. if (_textureCache.TryGetValue(key, out cachedImage))
  297. {
  298. return true;
  299. }
  300. cachedImage = null;
  301. return false;
  302. }
  303. public void Bind(long key, int index, GalImage image)
  304. {
  305. if (_textureCache.TryGetValue(key, out ImageHandler cachedImage))
  306. {
  307. GL.ActiveTexture(TextureUnit.Texture0 + index);
  308. TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
  309. GL.BindTexture(target, cachedImage.Handle);
  310. int[] swizzleRgba = new int[]
  311. {
  312. (int)OglEnumConverter.GetTextureSwizzle(image.XSource),
  313. (int)OglEnumConverter.GetTextureSwizzle(image.YSource),
  314. (int)OglEnumConverter.GetTextureSwizzle(image.ZSource),
  315. (int)OglEnumConverter.GetTextureSwizzle(image.WSource)
  316. };
  317. GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba);
  318. }
  319. }
  320. public void SetSampler(GalImage image, GalTextureSampler sampler)
  321. {
  322. int wrapS = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressU);
  323. int wrapT = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressV);
  324. int wrapR = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressP);
  325. int minFilter = (int)OglEnumConverter.GetTextureMinFilter(sampler.MinFilter, sampler.MipFilter);
  326. int magFilter = (int)OglEnumConverter.GetTextureMagFilter(sampler.MagFilter);
  327. TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
  328. GL.TexParameter(target, TextureParameterName.TextureWrapS, wrapS);
  329. GL.TexParameter(target, TextureParameterName.TextureWrapT, wrapT);
  330. GL.TexParameter(target, TextureParameterName.TextureWrapR, wrapR);
  331. GL.TexParameter(target, TextureParameterName.TextureMinFilter, minFilter);
  332. GL.TexParameter(target, TextureParameterName.TextureMagFilter, magFilter);
  333. float[] color = new float[]
  334. {
  335. sampler.BorderColor.Red,
  336. sampler.BorderColor.Green,
  337. sampler.BorderColor.Blue,
  338. sampler.BorderColor.Alpha
  339. };
  340. GL.TexParameter(target, TextureParameterName.TextureBorderColor, color);
  341. if (sampler.DepthCompare)
  342. {
  343. GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.CompareRToTexture);
  344. GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)OglEnumConverter.GetDepthCompareFunc(sampler.DepthCompareFunc));
  345. }
  346. else
  347. {
  348. GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.None);
  349. GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)All.Never);
  350. }
  351. }
  352. }
  353. }