TextureCopy.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. using OpenTK.Graphics.OpenGL;
  2. using Ryujinx.Common;
  3. using Ryujinx.Graphics.GAL;
  4. using System;
  5. namespace Ryujinx.Graphics.OpenGL.Image
  6. {
  7. class TextureCopy : IDisposable
  8. {
  9. private readonly Renderer _renderer;
  10. private int _srcFramebuffer;
  11. private int _dstFramebuffer;
  12. private int _copyPboHandle;
  13. private int _copyPboSize;
  14. public TextureCopy(Renderer renderer)
  15. {
  16. _renderer = renderer;
  17. }
  18. public void Copy(
  19. TextureView src,
  20. TextureView dst,
  21. Extents2D srcRegion,
  22. Extents2D dstRegion,
  23. bool linearFilter)
  24. {
  25. TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src;
  26. (int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers();
  27. GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy());
  28. GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy());
  29. int levels = Math.Min(src.Info.Levels, dst.Info.Levels);
  30. int layers = Math.Min(src.Info.GetLayers(), dst.Info.GetLayers());
  31. for (int level = 0; level < levels; level++)
  32. {
  33. for (int layer = 0; layer < layers; layer++)
  34. {
  35. if (layers > 1)
  36. {
  37. Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level, layer);
  38. Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level, layer);
  39. }
  40. else
  41. {
  42. Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level);
  43. Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level);
  44. }
  45. ClearBufferMask mask = GetMask(src.Format);
  46. if ((mask & (ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit)) != 0 || src.Format.IsInteger())
  47. {
  48. linearFilter = false;
  49. }
  50. BlitFramebufferFilter filter = linearFilter
  51. ? BlitFramebufferFilter.Linear
  52. : BlitFramebufferFilter.Nearest;
  53. GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
  54. GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
  55. GL.Disable(EnableCap.RasterizerDiscard);
  56. GL.Disable(IndexedEnableCap.ScissorTest, 0);
  57. GL.BlitFramebuffer(
  58. srcRegion.X1,
  59. srcRegion.Y1,
  60. srcRegion.X2,
  61. srcRegion.Y2,
  62. dstRegion.X1,
  63. dstRegion.Y1,
  64. dstRegion.X2,
  65. dstRegion.Y2,
  66. mask,
  67. filter);
  68. }
  69. if (level < levels - 1)
  70. {
  71. srcRegion = srcRegion.Reduce(1);
  72. dstRegion = dstRegion.Reduce(1);
  73. }
  74. }
  75. Attach(FramebufferTarget.ReadFramebuffer, src.Format, 0);
  76. Attach(FramebufferTarget.DrawFramebuffer, dst.Format, 0);
  77. GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle);
  78. GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
  79. ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable();
  80. ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard();
  81. if (srcConverted != src)
  82. {
  83. srcConverted.Dispose();
  84. }
  85. }
  86. public void CopyUnscaled(
  87. ITextureInfo src,
  88. ITextureInfo dst,
  89. int srcLayer,
  90. int dstLayer,
  91. int srcLevel,
  92. int dstLevel)
  93. {
  94. TextureCreateInfo srcInfo = src.Info;
  95. TextureCreateInfo dstInfo = dst.Info;
  96. int srcDepth = srcInfo.GetDepthOrLayers();
  97. int srcLevels = srcInfo.Levels;
  98. int dstDepth = dstInfo.GetDepthOrLayers();
  99. int dstLevels = dstInfo.Levels;
  100. if (dstInfo.Target == Target.Texture3D)
  101. {
  102. dstDepth = Math.Max(1, dstDepth >> dstLevel);
  103. }
  104. int depth = Math.Min(srcDepth, dstDepth);
  105. int levels = Math.Min(srcLevels, dstLevels);
  106. CopyUnscaled(src, dst, srcLayer, dstLayer, srcLevel, dstLevel, depth, levels);
  107. }
  108. public void CopyUnscaled(
  109. ITextureInfo src,
  110. ITextureInfo dst,
  111. int srcLayer,
  112. int dstLayer,
  113. int srcLevel,
  114. int dstLevel,
  115. int depth,
  116. int levels)
  117. {
  118. TextureCreateInfo srcInfo = src.Info;
  119. TextureCreateInfo dstInfo = dst.Info;
  120. int srcHandle = src.Handle;
  121. int dstHandle = dst.Handle;
  122. int srcWidth = srcInfo.Width;
  123. int srcHeight = srcInfo.Height;
  124. int dstWidth = dstInfo.Width;
  125. int dstHeight = dstInfo.Height;
  126. srcWidth = Math.Max(1, srcWidth >> srcLevel);
  127. srcHeight = Math.Max(1, srcHeight >> srcLevel);
  128. dstWidth = Math.Max(1, dstWidth >> dstLevel);
  129. dstHeight = Math.Max(1, dstHeight >> dstLevel);
  130. int blockWidth = 1;
  131. int blockHeight = 1;
  132. bool sizeInBlocks = false;
  133. // When copying from a compressed to a non-compressed format,
  134. // the non-compressed texture will have the size of the texture
  135. // in blocks (not in texels), so we must adjust that size to
  136. // match the size in texels of the compressed texture.
  137. if (!srcInfo.IsCompressed && dstInfo.IsCompressed)
  138. {
  139. srcWidth *= dstInfo.BlockWidth;
  140. srcHeight *= dstInfo.BlockHeight;
  141. blockWidth = dstInfo.BlockWidth;
  142. blockHeight = dstInfo.BlockHeight;
  143. sizeInBlocks = true;
  144. }
  145. else if (srcInfo.IsCompressed && !dstInfo.IsCompressed)
  146. {
  147. dstWidth *= srcInfo.BlockWidth;
  148. dstHeight *= srcInfo.BlockHeight;
  149. blockWidth = srcInfo.BlockWidth;
  150. blockHeight = srcInfo.BlockHeight;
  151. }
  152. int width = Math.Min(srcWidth, dstWidth);
  153. int height = Math.Min(srcHeight, dstHeight);
  154. for (int level = 0; level < levels; level++)
  155. {
  156. // Stop copy if we are already out of the levels range.
  157. if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels)
  158. {
  159. break;
  160. }
  161. if ((width % blockWidth != 0 || height % blockHeight != 0) && src is TextureView srcView && dst is TextureView dstView)
  162. {
  163. PboCopy(srcView, dstView, srcLayer, dstLayer, srcLevel + level, dstLevel + level, width, height);
  164. }
  165. else
  166. {
  167. int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
  168. int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
  169. if (HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows)
  170. {
  171. GL.CopyImageSubData(
  172. src.Storage.Handle,
  173. src.Storage.Info.Target.ConvertToImageTarget(),
  174. src.FirstLevel + srcLevel + level,
  175. 0,
  176. 0,
  177. src.FirstLayer + srcLayer,
  178. dst.Storage.Handle,
  179. dst.Storage.Info.Target.ConvertToImageTarget(),
  180. dst.FirstLevel + dstLevel + level,
  181. 0,
  182. 0,
  183. dst.FirstLayer + dstLayer,
  184. copyWidth,
  185. copyHeight,
  186. depth);
  187. }
  188. else
  189. {
  190. GL.CopyImageSubData(
  191. srcHandle,
  192. srcInfo.Target.ConvertToImageTarget(),
  193. srcLevel + level,
  194. 0,
  195. 0,
  196. srcLayer,
  197. dstHandle,
  198. dstInfo.Target.ConvertToImageTarget(),
  199. dstLevel + level,
  200. 0,
  201. 0,
  202. dstLayer,
  203. copyWidth,
  204. copyHeight,
  205. depth);
  206. }
  207. }
  208. width = Math.Max(1, width >> 1);
  209. height = Math.Max(1, height >> 1);
  210. if (srcInfo.Target == Target.Texture3D)
  211. {
  212. depth = Math.Max(1, depth >> 1);
  213. }
  214. }
  215. }
  216. private static FramebufferAttachment AttachmentForFormat(Format format)
  217. {
  218. if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint)
  219. {
  220. return FramebufferAttachment.DepthStencilAttachment;
  221. }
  222. else if (IsDepthOnly(format))
  223. {
  224. return FramebufferAttachment.DepthAttachment;
  225. }
  226. else if (format == Format.S8Uint)
  227. {
  228. return FramebufferAttachment.StencilAttachment;
  229. }
  230. else
  231. {
  232. return FramebufferAttachment.ColorAttachment0;
  233. }
  234. }
  235. private static void Attach(FramebufferTarget target, Format format, int handle, int level = 0)
  236. {
  237. FramebufferAttachment attachment = AttachmentForFormat(format);
  238. GL.FramebufferTexture(target, attachment, handle, level);
  239. }
  240. private static void Attach(FramebufferTarget target, Format format, int handle, int level, int layer)
  241. {
  242. FramebufferAttachment attachment = AttachmentForFormat(format);
  243. GL.FramebufferTextureLayer(target, attachment, handle, level, layer);
  244. }
  245. private static ClearBufferMask GetMask(Format format)
  246. {
  247. if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint || format == Format.S8UintD24Unorm)
  248. {
  249. return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit;
  250. }
  251. else if (IsDepthOnly(format))
  252. {
  253. return ClearBufferMask.DepthBufferBit;
  254. }
  255. else if (format == Format.S8Uint)
  256. {
  257. return ClearBufferMask.StencilBufferBit;
  258. }
  259. else
  260. {
  261. return ClearBufferMask.ColorBufferBit;
  262. }
  263. }
  264. private static bool IsDepthOnly(Format format)
  265. {
  266. return format == Format.D16Unorm || format == Format.D32Float;
  267. }
  268. public TextureView BgraSwap(TextureView from)
  269. {
  270. TextureView to = (TextureView)_renderer.CreateTexture(from.Info, from.ScaleFactor);
  271. EnsurePbo(from);
  272. GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle);
  273. from.WriteToPbo(0, forceBgra: true);
  274. GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
  275. GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPboHandle);
  276. to.ReadFromPbo(0, _copyPboSize);
  277. GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
  278. return to;
  279. }
  280. private TextureView PboCopy(TextureView from, TextureView to, int srcLayer, int dstLayer, int srcLevel, int dstLevel, int width, int height)
  281. {
  282. int dstWidth = width;
  283. int dstHeight = height;
  284. // The size of the source texture.
  285. int unpackWidth = from.Width;
  286. int unpackHeight = from.Height;
  287. if (from.Info.IsCompressed != to.Info.IsCompressed)
  288. {
  289. if (from.Info.IsCompressed)
  290. {
  291. // Dest size is in pixels, but should be in blocks
  292. dstWidth = BitUtils.DivRoundUp(width, from.Info.BlockWidth);
  293. dstHeight = BitUtils.DivRoundUp(height, from.Info.BlockHeight);
  294. // When copying from a compressed texture, the source size must be taken in blocks for unpacking to the uncompressed block texture.
  295. unpackWidth = BitUtils.DivRoundUp(from.Info.Width, from.Info.BlockWidth);
  296. unpackHeight = BitUtils.DivRoundUp(from.Info.Height, from.Info.BlockHeight);
  297. }
  298. else
  299. {
  300. // When copying to a compressed texture, the source size must be scaled by the block width for unpacking on the compressed target.
  301. unpackWidth = from.Info.Width * to.Info.BlockWidth;
  302. unpackHeight = from.Info.Height * to.Info.BlockHeight;
  303. }
  304. }
  305. EnsurePbo(from);
  306. GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle);
  307. // The source texture is written out in full, then the destination is taken as a slice from the data using unpack params.
  308. // The offset points to the base at which the requested layer is at.
  309. int offset = from.WriteToPbo2D(0, srcLayer, srcLevel);
  310. // If the destination size is not an exact match for the source unpack parameters, we need to set them to slice the data correctly.
  311. bool slice = (unpackWidth != dstWidth || unpackHeight != dstHeight);
  312. if (slice)
  313. {
  314. // Set unpack parameters to take a slice of width/height:
  315. GL.PixelStore(PixelStoreParameter.UnpackRowLength, unpackWidth);
  316. GL.PixelStore(PixelStoreParameter.UnpackImageHeight, unpackHeight);
  317. if (to.Info.IsCompressed)
  318. {
  319. GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockWidth, to.Info.BlockWidth);
  320. GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockHeight, to.Info.BlockHeight);
  321. GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockDepth, 1);
  322. GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockSize, to.Info.BytesPerPixel);
  323. }
  324. }
  325. GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
  326. GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPboHandle);
  327. to.ReadFromPbo2D(offset, dstLayer, dstLevel, dstWidth, dstHeight);
  328. if (slice)
  329. {
  330. // Reset unpack parameters
  331. GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0);
  332. GL.PixelStore(PixelStoreParameter.UnpackImageHeight, 0);
  333. if (to.Info.IsCompressed)
  334. {
  335. GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockWidth, 0);
  336. GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockHeight, 0);
  337. GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockDepth, 0);
  338. GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockSize, 0);
  339. }
  340. }
  341. GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
  342. return to;
  343. }
  344. private void EnsurePbo(TextureView view)
  345. {
  346. int requiredSize = 0;
  347. for (int level = 0; level < view.Info.Levels; level++)
  348. {
  349. requiredSize += view.Info.GetMipSize(level);
  350. }
  351. if (_copyPboSize < requiredSize && _copyPboHandle != 0)
  352. {
  353. GL.DeleteBuffer(_copyPboHandle);
  354. _copyPboHandle = 0;
  355. }
  356. if (_copyPboHandle == 0)
  357. {
  358. _copyPboHandle = GL.GenBuffer();
  359. _copyPboSize = requiredSize;
  360. GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle);
  361. GL.BufferData(BufferTarget.PixelPackBuffer, requiredSize, IntPtr.Zero, BufferUsageHint.DynamicCopy);
  362. }
  363. }
  364. private int GetSrcFramebufferLazy()
  365. {
  366. if (_srcFramebuffer == 0)
  367. {
  368. _srcFramebuffer = GL.GenFramebuffer();
  369. }
  370. return _srcFramebuffer;
  371. }
  372. private int GetDstFramebufferLazy()
  373. {
  374. if (_dstFramebuffer == 0)
  375. {
  376. _dstFramebuffer = GL.GenFramebuffer();
  377. }
  378. return _dstFramebuffer;
  379. }
  380. public void Dispose()
  381. {
  382. if (_srcFramebuffer != 0)
  383. {
  384. GL.DeleteFramebuffer(_srcFramebuffer);
  385. _srcFramebuffer = 0;
  386. }
  387. if (_dstFramebuffer != 0)
  388. {
  389. GL.DeleteFramebuffer(_dstFramebuffer);
  390. _dstFramebuffer = 0;
  391. }
  392. if (_copyPboHandle != 0)
  393. {
  394. GL.DeleteBuffer(_copyPboHandle);
  395. _copyPboHandle = 0;
  396. }
  397. }
  398. }
  399. }