OglTexture.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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. case TextureTarget.Texture2DArray:
  82. GL.TexImage3D(
  83. target,
  84. level,
  85. internalFmt,
  86. image.Width,
  87. image.Height,
  88. image.LayerCount,
  89. border,
  90. format,
  91. type,
  92. IntPtr.Zero);
  93. break;
  94. default:
  95. throw new NotImplementedException($"Unsupported texture target type: {target}");
  96. }
  97. }
  98. public void Create(long key, byte[] data, GalImage image)
  99. {
  100. int handle = GL.GenTexture();
  101. TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
  102. GL.BindTexture(target, handle);
  103. const int level = 0; //TODO: Support mipmap textures.
  104. const int border = 0;
  105. _textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)data.Length);
  106. if (ImageUtils.IsCompressed(image.Format) && !IsAstc(image.Format))
  107. {
  108. InternalFormat internalFmt = OglEnumConverter.GetCompressedImageFormat(image.Format);
  109. switch (target)
  110. {
  111. case TextureTarget.Texture1D:
  112. GL.CompressedTexImage1D(
  113. target,
  114. level,
  115. internalFmt,
  116. image.Width,
  117. border,
  118. data.Length,
  119. data);
  120. break;
  121. case TextureTarget.Texture2D:
  122. GL.CompressedTexImage2D(
  123. target,
  124. level,
  125. internalFmt,
  126. image.Width,
  127. image.Height,
  128. border,
  129. data.Length,
  130. data);
  131. break;
  132. case TextureTarget.Texture3D:
  133. GL.CompressedTexImage3D(
  134. target,
  135. level,
  136. internalFmt,
  137. image.Width,
  138. image.Height,
  139. image.Depth,
  140. border,
  141. data.Length,
  142. data);
  143. break;
  144. // Cube map arrays are just 2D texture arrays with 6 entries
  145. // per cube map so we can handle them in the same way
  146. case TextureTarget.TextureCubeMapArray:
  147. case TextureTarget.Texture2DArray:
  148. GL.CompressedTexImage3D(
  149. target,
  150. level,
  151. internalFmt,
  152. image.Width,
  153. image.Height,
  154. image.LayerCount,
  155. border,
  156. data.Length,
  157. data);
  158. break;
  159. case TextureTarget.TextureCubeMap:
  160. Span<byte> array = new Span<byte>(data);
  161. int faceSize = ImageUtils.GetSize(image) / 6;
  162. for (int Face = 0; Face < 6; Face++)
  163. {
  164. GL.CompressedTexImage2D(
  165. TextureTarget.TextureCubeMapPositiveX + Face,
  166. level,
  167. internalFmt,
  168. image.Width,
  169. image.Height,
  170. border,
  171. faceSize,
  172. array.Slice(Face * faceSize, faceSize).ToArray());
  173. }
  174. break;
  175. default:
  176. throw new NotImplementedException($"Unsupported texture target type: {target}");
  177. }
  178. }
  179. else
  180. {
  181. //TODO: Use KHR_texture_compression_astc_hdr when available
  182. if (IsAstc(image.Format))
  183. {
  184. int textureBlockWidth = ImageUtils.GetBlockWidth(image.Format);
  185. int textureBlockHeight = ImageUtils.GetBlockHeight(image.Format);
  186. int textureBlockDepth = ImageUtils.GetBlockDepth(image.Format);
  187. data = AstcDecoder.DecodeToRgba8888(
  188. data,
  189. textureBlockWidth,
  190. textureBlockHeight,
  191. textureBlockDepth,
  192. image.Width,
  193. image.Height,
  194. image.Depth);
  195. image.Format = GalImageFormat.Rgba8 | (image.Format & GalImageFormat.TypeMask);
  196. }
  197. (PixelInternalFormat internalFmt,
  198. PixelFormat format,
  199. PixelType type) = OglEnumConverter.GetImageFormat(image.Format);
  200. switch (target)
  201. {
  202. case TextureTarget.Texture1D:
  203. GL.TexImage1D(
  204. target,
  205. level,
  206. internalFmt,
  207. image.Width,
  208. border,
  209. format,
  210. type,
  211. data);
  212. break;
  213. case TextureTarget.Texture2D:
  214. GL.TexImage2D(
  215. target,
  216. level,
  217. internalFmt,
  218. image.Width,
  219. image.Height,
  220. border,
  221. format,
  222. type,
  223. data);
  224. break;
  225. case TextureTarget.Texture3D:
  226. GL.TexImage3D(
  227. target,
  228. level,
  229. internalFmt,
  230. image.Width,
  231. image.Height,
  232. image.Depth,
  233. border,
  234. format,
  235. type,
  236. data);
  237. break;
  238. // Cube map arrays are just 2D texture arrays with 6 entries
  239. // per cube map so we can handle them in the same way
  240. case TextureTarget.TextureCubeMapArray:
  241. case TextureTarget.Texture2DArray:
  242. GL.TexImage3D(
  243. target,
  244. level,
  245. internalFmt,
  246. image.Width,
  247. image.Height,
  248. image.LayerCount,
  249. border,
  250. format,
  251. type,
  252. data);
  253. break;
  254. case TextureTarget.TextureCubeMap:
  255. Span<byte> array = new Span<byte>(data);
  256. int faceSize = ImageUtils.GetSize(image) / 6;
  257. for (int face = 0; face < 6; face++)
  258. {
  259. GL.TexImage2D(
  260. TextureTarget.TextureCubeMapPositiveX + face,
  261. level,
  262. internalFmt,
  263. image.Width,
  264. image.Height,
  265. border,
  266. format,
  267. type,
  268. array.Slice(face * faceSize, faceSize).ToArray());
  269. }
  270. break;
  271. default:
  272. throw new NotImplementedException($"Unsupported texture target type: {target}");
  273. }
  274. }
  275. }
  276. private static bool IsAstc(GalImageFormat format)
  277. {
  278. format &= GalImageFormat.FormatMask;
  279. return format > GalImageFormat.Astc2DStart && format < GalImageFormat.Astc2DEnd;
  280. }
  281. public bool TryGetImage(long key, out GalImage image)
  282. {
  283. if (_textureCache.TryGetValue(key, out ImageHandler cachedImage))
  284. {
  285. image = cachedImage.Image;
  286. return true;
  287. }
  288. image = default(GalImage);
  289. return false;
  290. }
  291. public bool TryGetImageHandler(long key, out ImageHandler cachedImage)
  292. {
  293. if (_textureCache.TryGetValue(key, out cachedImage))
  294. {
  295. return true;
  296. }
  297. cachedImage = null;
  298. return false;
  299. }
  300. public void Bind(long key, int index, GalImage image)
  301. {
  302. if (_textureCache.TryGetValue(key, out ImageHandler cachedImage))
  303. {
  304. GL.ActiveTexture(TextureUnit.Texture0 + index);
  305. TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
  306. GL.BindTexture(target, cachedImage.Handle);
  307. int[] swizzleRgba = new int[]
  308. {
  309. (int)OglEnumConverter.GetTextureSwizzle(image.XSource),
  310. (int)OglEnumConverter.GetTextureSwizzle(image.YSource),
  311. (int)OglEnumConverter.GetTextureSwizzle(image.ZSource),
  312. (int)OglEnumConverter.GetTextureSwizzle(image.WSource)
  313. };
  314. GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba);
  315. }
  316. }
  317. public void SetSampler(GalImage image, GalTextureSampler sampler)
  318. {
  319. int wrapS = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressU);
  320. int wrapT = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressV);
  321. int wrapR = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressP);
  322. int minFilter = (int)OglEnumConverter.GetTextureMinFilter(sampler.MinFilter, sampler.MipFilter);
  323. int magFilter = (int)OglEnumConverter.GetTextureMagFilter(sampler.MagFilter);
  324. TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
  325. GL.TexParameter(target, TextureParameterName.TextureWrapS, wrapS);
  326. GL.TexParameter(target, TextureParameterName.TextureWrapT, wrapT);
  327. GL.TexParameter(target, TextureParameterName.TextureWrapR, wrapR);
  328. GL.TexParameter(target, TextureParameterName.TextureMinFilter, minFilter);
  329. GL.TexParameter(target, TextureParameterName.TextureMagFilter, magFilter);
  330. float[] color = new float[]
  331. {
  332. sampler.BorderColor.Red,
  333. sampler.BorderColor.Green,
  334. sampler.BorderColor.Blue,
  335. sampler.BorderColor.Alpha
  336. };
  337. GL.TexParameter(target, TextureParameterName.TextureBorderColor, color);
  338. if (sampler.DepthCompare)
  339. {
  340. GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.CompareRToTexture);
  341. GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)OglEnumConverter.GetDepthCompareFunc(sampler.DepthCompareFunc));
  342. }
  343. else
  344. {
  345. GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.None);
  346. GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)All.Never);
  347. }
  348. }
  349. }
  350. }