TextureCopy.cs 14 KB

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