TextureView.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  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, ITextureInfo
  7. {
  8. private readonly Renderer _renderer;
  9. private readonly TextureStorage _parent;
  10. public int StorageHandle => _parent.Handle;
  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. firstLayer += FirstLayer;
  78. firstLevel += FirstLevel;
  79. return _parent.CreateView(info, firstLayer, firstLevel);
  80. }
  81. public int GetIncompatibleFormatViewHandle()
  82. {
  83. // AMD and Intel have a bug where the view format is always ignored;
  84. // they use the parent format instead.
  85. // As a workaround we create a new texture with the correct
  86. // format, and then do a copy after the draw.
  87. if (_parent.Info.Format != Format)
  88. {
  89. if (_incompatibleFormatView == null)
  90. {
  91. _incompatibleFormatView = (TextureView)_renderer.CreateTexture(Info, ScaleFactor);
  92. }
  93. _renderer.TextureCopy.CopyUnscaled(_parent, _incompatibleFormatView, FirstLayer, 0, FirstLevel, 0);
  94. return _incompatibleFormatView.Handle;
  95. }
  96. return Handle;
  97. }
  98. public void SignalModified()
  99. {
  100. if (_incompatibleFormatView != null)
  101. {
  102. _renderer.TextureCopy.CopyUnscaled(_incompatibleFormatView, _parent, 0, FirstLayer, 0, FirstLevel);
  103. }
  104. }
  105. public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
  106. {
  107. TextureView destinationView = (TextureView)destination;
  108. _renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
  109. }
  110. public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel)
  111. {
  112. TextureView destinationView = (TextureView)destination;
  113. _renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
  114. }
  115. public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
  116. {
  117. _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
  118. }
  119. public byte[] GetData()
  120. {
  121. int size = 0;
  122. for (int level = 0; level < Info.Levels; level++)
  123. {
  124. size += Info.GetMipSize(level);
  125. }
  126. byte[] data = new byte[size];
  127. unsafe
  128. {
  129. fixed (byte* ptr = data)
  130. {
  131. WriteTo((IntPtr)ptr);
  132. }
  133. }
  134. return data;
  135. }
  136. public void WriteToPbo(int offset, bool forceBgra)
  137. {
  138. WriteTo(IntPtr.Zero + offset, forceBgra);
  139. }
  140. public int WriteToPbo2D(int offset, int layer, int level)
  141. {
  142. return WriteTo2D(IntPtr.Zero + offset, layer, level);
  143. }
  144. private int WriteTo2D(IntPtr data, int layer, int level)
  145. {
  146. TextureTarget target = Target.Convert();
  147. Bind(target, 0);
  148. FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
  149. PixelFormat pixelFormat = format.PixelFormat;
  150. PixelType pixelType = format.PixelType;
  151. if (target == TextureTarget.TextureCubeMap || target == TextureTarget.TextureCubeMapArray)
  152. {
  153. target = TextureTarget.TextureCubeMapPositiveX + (layer % 6);
  154. }
  155. int mipSize = Info.GetMipSize2D(level);
  156. // The GL function returns all layers. Must return the offset of the layer we're interested in.
  157. int resultOffset = target switch
  158. {
  159. TextureTarget.TextureCubeMapArray => (layer / 6) * mipSize,
  160. TextureTarget.Texture1DArray => layer * mipSize,
  161. TextureTarget.Texture2DArray => layer * mipSize,
  162. _ => 0
  163. };
  164. if (format.IsCompressed)
  165. {
  166. GL.GetCompressedTexImage(target, level, data);
  167. }
  168. else
  169. {
  170. GL.GetTexImage(target, level, pixelFormat, pixelType, data);
  171. }
  172. return resultOffset;
  173. }
  174. private void WriteTo(IntPtr data, bool forceBgra = false)
  175. {
  176. TextureTarget target = Target.Convert();
  177. Bind(target, 0);
  178. FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
  179. PixelFormat pixelFormat = format.PixelFormat;
  180. PixelType pixelType = format.PixelType;
  181. if (forceBgra)
  182. {
  183. pixelFormat = PixelFormat.Bgra;
  184. }
  185. int faces = 1;
  186. if (target == TextureTarget.TextureCubeMap)
  187. {
  188. target = TextureTarget.TextureCubeMapPositiveX;
  189. faces = 6;
  190. }
  191. for (int level = 0; level < Info.Levels; level++)
  192. {
  193. for (int face = 0; face < faces; face++)
  194. {
  195. int faceOffset = face * Info.GetMipSize2D(level);
  196. if (format.IsCompressed)
  197. {
  198. GL.GetCompressedTexImage(target + face, level, data + faceOffset);
  199. }
  200. else
  201. {
  202. GL.GetTexImage(target + face, level, pixelFormat, pixelType, data + faceOffset);
  203. }
  204. }
  205. data += Info.GetMipSize(level);
  206. }
  207. }
  208. public void SetData(ReadOnlySpan<byte> data)
  209. {
  210. unsafe
  211. {
  212. fixed (byte* ptr = data)
  213. {
  214. ReadFrom((IntPtr)ptr, data.Length);
  215. }
  216. }
  217. }
  218. public void SetData(ReadOnlySpan<byte> data, int layer, int level)
  219. {
  220. unsafe
  221. {
  222. fixed (byte* ptr = data)
  223. {
  224. int width = Math.Max(Info.Width >> level, 1);
  225. int height = Math.Max(Info.Height >> level, 1);
  226. ReadFrom2D((IntPtr)ptr, layer, level, width, height);
  227. }
  228. }
  229. }
  230. public void ReadFromPbo(int offset, int size)
  231. {
  232. ReadFrom(IntPtr.Zero + offset, size);
  233. }
  234. public void ReadFromPbo2D(int offset, int layer, int level, int width, int height)
  235. {
  236. ReadFrom2D(IntPtr.Zero + offset, layer, level, width, height);
  237. }
  238. private void ReadFrom2D(IntPtr data, int layer, int level, int width, int height)
  239. {
  240. TextureTarget target = Target.Convert();
  241. int mipSize = Info.GetMipSize2D(level);
  242. Bind(target, 0);
  243. FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
  244. switch (Target)
  245. {
  246. case Target.Texture1D:
  247. if (format.IsCompressed)
  248. {
  249. GL.CompressedTexSubImage1D(
  250. target,
  251. level,
  252. 0,
  253. width,
  254. format.PixelFormat,
  255. mipSize,
  256. data);
  257. }
  258. else
  259. {
  260. GL.TexSubImage1D(
  261. target,
  262. level,
  263. 0,
  264. width,
  265. format.PixelFormat,
  266. format.PixelType,
  267. data);
  268. }
  269. break;
  270. case Target.Texture1DArray:
  271. if (format.IsCompressed)
  272. {
  273. GL.CompressedTexSubImage2D(
  274. target,
  275. level,
  276. 0,
  277. layer,
  278. width,
  279. 1,
  280. format.PixelFormat,
  281. mipSize,
  282. data);
  283. }
  284. else
  285. {
  286. GL.TexSubImage2D(
  287. target,
  288. level,
  289. 0,
  290. layer,
  291. width,
  292. 1,
  293. format.PixelFormat,
  294. format.PixelType,
  295. data);
  296. }
  297. break;
  298. case Target.Texture2D:
  299. if (format.IsCompressed)
  300. {
  301. GL.CompressedTexSubImage2D(
  302. target,
  303. level,
  304. 0,
  305. 0,
  306. width,
  307. height,
  308. format.PixelFormat,
  309. mipSize,
  310. data);
  311. }
  312. else
  313. {
  314. GL.TexSubImage2D(
  315. target,
  316. level,
  317. 0,
  318. 0,
  319. width,
  320. height,
  321. format.PixelFormat,
  322. format.PixelType,
  323. data);
  324. }
  325. break;
  326. case Target.Texture2DArray:
  327. case Target.Texture3D:
  328. case Target.CubemapArray:
  329. if (format.IsCompressed)
  330. {
  331. GL.CompressedTexSubImage3D(
  332. target,
  333. level,
  334. 0,
  335. 0,
  336. layer,
  337. width,
  338. height,
  339. 1,
  340. format.PixelFormat,
  341. mipSize,
  342. data);
  343. }
  344. else
  345. {
  346. GL.TexSubImage3D(
  347. target,
  348. level,
  349. 0,
  350. 0,
  351. layer,
  352. width,
  353. height,
  354. 1,
  355. format.PixelFormat,
  356. format.PixelType,
  357. data);
  358. }
  359. break;
  360. case Target.Cubemap:
  361. if (format.IsCompressed)
  362. {
  363. GL.CompressedTexSubImage2D(
  364. TextureTarget.TextureCubeMapPositiveX + layer,
  365. level,
  366. 0,
  367. 0,
  368. width,
  369. height,
  370. format.PixelFormat,
  371. mipSize,
  372. data);
  373. }
  374. else
  375. {
  376. GL.TexSubImage2D(
  377. TextureTarget.TextureCubeMapPositiveX + layer,
  378. level,
  379. 0,
  380. 0,
  381. width,
  382. height,
  383. format.PixelFormat,
  384. format.PixelType,
  385. data);
  386. }
  387. break;
  388. }
  389. }
  390. private void ReadFrom(IntPtr data, int size)
  391. {
  392. TextureTarget target = Target.Convert();
  393. Bind(target, 0);
  394. FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
  395. int width = Info.Width;
  396. int height = Info.Height;
  397. int depth = Info.Depth;
  398. int offset = 0;
  399. for (int level = 0; level < Info.Levels; level++)
  400. {
  401. int mipSize = Info.GetMipSize(level);
  402. int endOffset = offset + mipSize;
  403. if ((uint)endOffset > (uint)size)
  404. {
  405. return;
  406. }
  407. switch (Info.Target)
  408. {
  409. case Target.Texture1D:
  410. if (format.IsCompressed)
  411. {
  412. GL.CompressedTexSubImage1D(
  413. target,
  414. level,
  415. 0,
  416. width,
  417. format.PixelFormat,
  418. mipSize,
  419. data);
  420. }
  421. else
  422. {
  423. GL.TexSubImage1D(
  424. target,
  425. level,
  426. 0,
  427. width,
  428. format.PixelFormat,
  429. format.PixelType,
  430. data);
  431. }
  432. break;
  433. case Target.Texture1DArray:
  434. case Target.Texture2D:
  435. if (format.IsCompressed)
  436. {
  437. GL.CompressedTexSubImage2D(
  438. target,
  439. level,
  440. 0,
  441. 0,
  442. width,
  443. height,
  444. format.PixelFormat,
  445. mipSize,
  446. data);
  447. }
  448. else
  449. {
  450. GL.TexSubImage2D(
  451. target,
  452. level,
  453. 0,
  454. 0,
  455. width,
  456. height,
  457. format.PixelFormat,
  458. format.PixelType,
  459. data);
  460. }
  461. break;
  462. case Target.Texture2DArray:
  463. case Target.Texture3D:
  464. case Target.CubemapArray:
  465. if (format.IsCompressed)
  466. {
  467. GL.CompressedTexSubImage3D(
  468. target,
  469. level,
  470. 0,
  471. 0,
  472. 0,
  473. width,
  474. height,
  475. depth,
  476. format.PixelFormat,
  477. mipSize,
  478. data);
  479. }
  480. else
  481. {
  482. GL.TexSubImage3D(
  483. target,
  484. level,
  485. 0,
  486. 0,
  487. 0,
  488. width,
  489. height,
  490. depth,
  491. format.PixelFormat,
  492. format.PixelType,
  493. data);
  494. }
  495. break;
  496. case Target.Cubemap:
  497. int faceOffset = 0;
  498. for (int face = 0; face < 6; face++, faceOffset += mipSize / 6)
  499. {
  500. if (format.IsCompressed)
  501. {
  502. GL.CompressedTexSubImage2D(
  503. TextureTarget.TextureCubeMapPositiveX + face,
  504. level,
  505. 0,
  506. 0,
  507. width,
  508. height,
  509. format.PixelFormat,
  510. mipSize / 6,
  511. data + faceOffset);
  512. }
  513. else
  514. {
  515. GL.TexSubImage2D(
  516. TextureTarget.TextureCubeMapPositiveX + face,
  517. level,
  518. 0,
  519. 0,
  520. width,
  521. height,
  522. format.PixelFormat,
  523. format.PixelType,
  524. data + faceOffset);
  525. }
  526. }
  527. break;
  528. }
  529. data += mipSize;
  530. offset += mipSize;
  531. width = Math.Max(1, width >> 1);
  532. height = Math.Max(1, height >> 1);
  533. if (Target == Target.Texture3D)
  534. {
  535. depth = Math.Max(1, depth >> 1);
  536. }
  537. }
  538. }
  539. public void SetStorage(BufferRange buffer)
  540. {
  541. throw new NotSupportedException();
  542. }
  543. private void DisposeHandles()
  544. {
  545. if (_incompatibleFormatView != null)
  546. {
  547. _incompatibleFormatView.Dispose();
  548. _incompatibleFormatView = null;
  549. }
  550. if (Handle != 0)
  551. {
  552. GL.DeleteTexture(Handle);
  553. Handle = 0;
  554. }
  555. }
  556. /// <summary>
  557. /// Release the view without necessarily disposing the parent if we are the default view.
  558. /// This allows it to be added to the resource pool and reused later.
  559. /// </summary>
  560. public void Release()
  561. {
  562. bool hadHandle = Handle != 0;
  563. if (_parent.DefaultView != this)
  564. {
  565. DisposeHandles();
  566. }
  567. if (hadHandle)
  568. {
  569. _parent.DecrementViewsCount();
  570. }
  571. }
  572. public void Dispose()
  573. {
  574. if (_parent.DefaultView == this)
  575. {
  576. // Remove the default view (us), so that the texture cannot be released to the cache.
  577. _parent.DeleteDefault();
  578. }
  579. Release();
  580. }
  581. }
  582. }