TextureCopy.cs 18 KB

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