TextureCopy.cs 19 KB

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