TextureView.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. using OpenTK.Graphics.OpenGL;
  2. using Ryujinx.Graphics.GAL;
  3. using System;
  4. namespace Ryujinx.Graphics.OpenGL.Image
  5. {
  6. class TextureView : TextureBase, ITexture
  7. {
  8. private readonly Renderer _renderer;
  9. private readonly TextureStorage _parent;
  10. private TextureView _emulatedViewParent;
  11. private TextureView _incompatibleFormatView;
  12. public int FirstLayer { get; private set; }
  13. public int FirstLevel { get; private set; }
  14. public TextureView(
  15. Renderer renderer,
  16. TextureStorage parent,
  17. TextureCreateInfo info,
  18. int firstLayer,
  19. int firstLevel) : base(info, parent.ScaleFactor)
  20. {
  21. _renderer = renderer;
  22. _parent = parent;
  23. FirstLayer = firstLayer;
  24. FirstLevel = firstLevel;
  25. CreateView();
  26. }
  27. private void CreateView()
  28. {
  29. TextureTarget target = Target.Convert();
  30. FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
  31. PixelInternalFormat pixelInternalFormat;
  32. if (format.IsCompressed)
  33. {
  34. pixelInternalFormat = (PixelInternalFormat)format.PixelFormat;
  35. }
  36. else
  37. {
  38. pixelInternalFormat = format.PixelInternalFormat;
  39. }
  40. GL.TextureView(
  41. Handle,
  42. target,
  43. _parent.Handle,
  44. pixelInternalFormat,
  45. FirstLevel,
  46. Info.Levels,
  47. FirstLayer,
  48. Info.GetLayers());
  49. GL.ActiveTexture(TextureUnit.Texture0);
  50. GL.BindTexture(target, Handle);
  51. int[] swizzleRgba = new int[]
  52. {
  53. (int)Info.SwizzleR.Convert(),
  54. (int)Info.SwizzleG.Convert(),
  55. (int)Info.SwizzleB.Convert(),
  56. (int)Info.SwizzleA.Convert()
  57. };
  58. if (Info.Format.IsBgra8())
  59. {
  60. // Swap B <-> R for BGRA formats, as OpenGL has no support for them
  61. // and we need to manually swap the components on read/write on the GPU.
  62. int temp = swizzleRgba[0];
  63. swizzleRgba[0] = swizzleRgba[2];
  64. swizzleRgba[2] = temp;
  65. }
  66. GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba);
  67. int maxLevel = Info.Levels - 1;
  68. if (maxLevel < 0)
  69. {
  70. maxLevel = 0;
  71. }
  72. GL.TexParameter(target, TextureParameterName.TextureMaxLevel, maxLevel);
  73. GL.TexParameter(target, TextureParameterName.DepthStencilTextureMode, (int)Info.DepthStencilMode.Convert());
  74. }
  75. public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel)
  76. {
  77. if (Info.IsCompressed == info.IsCompressed)
  78. {
  79. firstLayer += FirstLayer;
  80. firstLevel += FirstLevel;
  81. return _parent.CreateView(info, firstLayer, firstLevel);
  82. }
  83. else
  84. {
  85. // TODO: Most graphics APIs doesn't support creating a texture view from a compressed format
  86. // with a non-compressed format (or vice-versa), however NVN seems to support it.
  87. // So we emulate that here with a texture copy (see the first CopyTo overload).
  88. // However right now it only does a single copy right after the view is created,
  89. // so it doesn't work for all cases.
  90. TextureView emulatedView = (TextureView)_renderer.CreateTexture(info, ScaleFactor);
  91. emulatedView._emulatedViewParent = this;
  92. emulatedView.FirstLayer = firstLayer;
  93. emulatedView.FirstLevel = firstLevel;
  94. return emulatedView;
  95. }
  96. }
  97. public int GetIncompatibleFormatViewHandle()
  98. {
  99. // AMD and Intel has a bug where the view format is always ignored,
  100. // it uses the parent format instead.
  101. // As workaround we create a new texture with the correct
  102. // format, and then do a copy after the draw.
  103. if (_parent.Info.Format != Format)
  104. {
  105. if (_incompatibleFormatView == null)
  106. {
  107. _incompatibleFormatView = (TextureView)_renderer.CreateTexture(Info, ScaleFactor);
  108. }
  109. TextureCopyUnscaled.Copy(_parent.Info, _incompatibleFormatView.Info, _parent.Handle, _incompatibleFormatView.Handle, FirstLayer, 0, FirstLevel, 0, ScaleFactor);
  110. return _incompatibleFormatView.Handle;
  111. }
  112. return Handle;
  113. }
  114. public void SignalModified()
  115. {
  116. if (_incompatibleFormatView != null)
  117. {
  118. TextureCopyUnscaled.Copy(_incompatibleFormatView.Info, _parent.Info, _incompatibleFormatView.Handle, _parent.Handle, 0, FirstLayer, 0, FirstLevel, ScaleFactor);
  119. }
  120. }
  121. public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
  122. {
  123. TextureView destinationView = (TextureView)destination;
  124. TextureCopyUnscaled.Copy(Info, destinationView.Info, Handle, destinationView.Handle, 0, firstLayer, 0, firstLevel, ScaleFactor);
  125. if (destinationView._emulatedViewParent != null)
  126. {
  127. TextureCopyUnscaled.Copy(
  128. Info,
  129. destinationView._emulatedViewParent.Info,
  130. Handle,
  131. destinationView._emulatedViewParent.Handle,
  132. 0,
  133. destinationView.FirstLayer,
  134. 0,
  135. destinationView.FirstLevel,
  136. ScaleFactor);
  137. }
  138. }
  139. public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
  140. {
  141. _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
  142. }
  143. public byte[] GetData()
  144. {
  145. int size = 0;
  146. for (int level = 0; level < Info.Levels; level++)
  147. {
  148. size += Info.GetMipSize(level);
  149. }
  150. byte[] data = new byte[size];
  151. unsafe
  152. {
  153. fixed (byte* ptr = data)
  154. {
  155. WriteTo((IntPtr)ptr);
  156. }
  157. }
  158. return data;
  159. }
  160. public void WriteToPbo(int offset, bool forceBgra)
  161. {
  162. WriteTo(IntPtr.Zero + offset, forceBgra);
  163. }
  164. private void WriteTo(IntPtr data, bool forceBgra = false)
  165. {
  166. TextureTarget target = Target.Convert();
  167. Bind(target, 0);
  168. FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
  169. PixelFormat pixelFormat = format.PixelFormat;
  170. PixelType pixelType = format.PixelType;
  171. if (forceBgra)
  172. {
  173. pixelFormat = PixelFormat.Bgra;
  174. }
  175. int faces = 1;
  176. if (target == TextureTarget.TextureCubeMap)
  177. {
  178. target = TextureTarget.TextureCubeMapPositiveX;
  179. faces = 6;
  180. }
  181. for (int level = 0; level < Info.Levels; level++)
  182. {
  183. for (int face = 0; face < faces; face++)
  184. {
  185. int faceOffset = face * Info.GetMipSize2D(level);
  186. if (format.IsCompressed)
  187. {
  188. GL.GetCompressedTexImage(target + face, level, data + faceOffset);
  189. }
  190. else
  191. {
  192. GL.GetTexImage(target + face, level, pixelFormat, pixelType, data + faceOffset);
  193. }
  194. }
  195. data += Info.GetMipSize(level);
  196. }
  197. }
  198. public void SetData(ReadOnlySpan<byte> data)
  199. {
  200. unsafe
  201. {
  202. fixed (byte* ptr = data)
  203. {
  204. ReadFrom((IntPtr)ptr, data.Length);
  205. }
  206. }
  207. }
  208. public void ReadFromPbo(int offset, int size)
  209. {
  210. ReadFrom(IntPtr.Zero + offset, size);
  211. }
  212. private void ReadFrom(IntPtr data, int size)
  213. {
  214. TextureTarget target = Target.Convert();
  215. Bind(target, 0);
  216. FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
  217. int width = Info.Width;
  218. int height = Info.Height;
  219. int depth = Info.Depth;
  220. int offset = 0;
  221. for (int level = 0; level < Info.Levels; level++)
  222. {
  223. int mipSize = Info.GetMipSize(level);
  224. int endOffset = offset + mipSize;
  225. if ((uint)endOffset > (uint)size)
  226. {
  227. return;
  228. }
  229. switch (Info.Target)
  230. {
  231. case Target.Texture1D:
  232. if (format.IsCompressed)
  233. {
  234. GL.CompressedTexSubImage1D(
  235. target,
  236. level,
  237. 0,
  238. width,
  239. format.PixelFormat,
  240. mipSize,
  241. data);
  242. }
  243. else
  244. {
  245. GL.TexSubImage1D(
  246. target,
  247. level,
  248. 0,
  249. width,
  250. format.PixelFormat,
  251. format.PixelType,
  252. data);
  253. }
  254. break;
  255. case Target.Texture1DArray:
  256. case Target.Texture2D:
  257. if (format.IsCompressed)
  258. {
  259. GL.CompressedTexSubImage2D(
  260. target,
  261. level,
  262. 0,
  263. 0,
  264. width,
  265. height,
  266. format.PixelFormat,
  267. mipSize,
  268. data);
  269. }
  270. else
  271. {
  272. GL.TexSubImage2D(
  273. target,
  274. level,
  275. 0,
  276. 0,
  277. width,
  278. height,
  279. format.PixelFormat,
  280. format.PixelType,
  281. data);
  282. }
  283. break;
  284. case Target.Texture2DArray:
  285. case Target.Texture3D:
  286. case Target.CubemapArray:
  287. if (format.IsCompressed)
  288. {
  289. GL.CompressedTexSubImage3D(
  290. target,
  291. level,
  292. 0,
  293. 0,
  294. 0,
  295. width,
  296. height,
  297. depth,
  298. format.PixelFormat,
  299. mipSize,
  300. data);
  301. }
  302. else
  303. {
  304. GL.TexSubImage3D(
  305. target,
  306. level,
  307. 0,
  308. 0,
  309. 0,
  310. width,
  311. height,
  312. depth,
  313. format.PixelFormat,
  314. format.PixelType,
  315. data);
  316. }
  317. break;
  318. case Target.Cubemap:
  319. int faceOffset = 0;
  320. for (int face = 0; face < 6; face++, faceOffset += mipSize / 6)
  321. {
  322. if (format.IsCompressed)
  323. {
  324. GL.CompressedTexSubImage2D(
  325. TextureTarget.TextureCubeMapPositiveX + face,
  326. level,
  327. 0,
  328. 0,
  329. width,
  330. height,
  331. format.PixelFormat,
  332. mipSize / 6,
  333. data + faceOffset);
  334. }
  335. else
  336. {
  337. GL.TexSubImage2D(
  338. TextureTarget.TextureCubeMapPositiveX + face,
  339. level,
  340. 0,
  341. 0,
  342. width,
  343. height,
  344. format.PixelFormat,
  345. format.PixelType,
  346. data + faceOffset);
  347. }
  348. }
  349. break;
  350. }
  351. data += mipSize;
  352. offset += mipSize;
  353. width = Math.Max(1, width >> 1);
  354. height = Math.Max(1, height >> 1);
  355. if (Target == Target.Texture3D)
  356. {
  357. depth = Math.Max(1, depth >> 1);
  358. }
  359. }
  360. }
  361. public void SetStorage(BufferRange buffer)
  362. {
  363. throw new NotSupportedException();
  364. }
  365. private void DisposeHandles()
  366. {
  367. if (_incompatibleFormatView != null)
  368. {
  369. _incompatibleFormatView.Dispose();
  370. _incompatibleFormatView = null;
  371. }
  372. if (Handle != 0)
  373. {
  374. GL.DeleteTexture(Handle);
  375. Handle = 0;
  376. }
  377. }
  378. /// <summary>
  379. /// Release the view without necessarily disposing the parent if we are the default view.
  380. /// This allows it to be added to the resource pool and reused later.
  381. /// </summary>
  382. public void Release()
  383. {
  384. bool hadHandle = Handle != 0;
  385. if (_parent.DefaultView != this)
  386. {
  387. DisposeHandles();
  388. }
  389. if (hadHandle)
  390. {
  391. _parent.DecrementViewsCount();
  392. }
  393. }
  394. public void Dispose()
  395. {
  396. if (_parent.DefaultView == this)
  397. {
  398. // Remove the default view (us), so that the texture cannot be released to the cache.
  399. _parent.DeleteDefault();
  400. }
  401. Release();
  402. }
  403. }
  404. }