using Ryujinx.Graphics.GAL; using OpenTK.Graphics.OpenGL; using System; namespace Ryujinx.Graphics.OpenGL.Image { class TextureCopy : IDisposable { private readonly Renderer _renderer; private int _srcFramebuffer; private int _dstFramebuffer; private int _copyPboHandle; private int _copyPboSize; public TextureCopy(Renderer renderer) { _renderer = renderer; } public void Copy( TextureView src, TextureView dst, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { TextureView srcConverted = src.Format.IsBgra8() != dst.Format.IsBgra8() ? BgraSwap(src) : src; (int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers(); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle); Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle); ClearBufferMask mask = GetMask(src.Format); if ((mask & (ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit)) != 0 || src.Format.IsInteger()) { linearFilter = false; } BlitFramebufferFilter filter = linearFilter ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest; GL.ReadBuffer(ReadBufferMode.ColorAttachment0); GL.DrawBuffer(DrawBufferMode.ColorAttachment0); GL.Disable(EnableCap.RasterizerDiscard); GL.Disable(IndexedEnableCap.ScissorTest, 0); GL.BlitFramebuffer( srcRegion.X1, srcRegion.Y1, srcRegion.X2, srcRegion.Y2, dstRegion.X1, dstRegion.Y1, dstRegion.X2, dstRegion.Y2, mask, filter); Attach(FramebufferTarget.ReadFramebuffer, src.Format, 0); Attach(FramebufferTarget.DrawFramebuffer, dst.Format, 0); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard(); if (srcConverted != src) { srcConverted.Dispose(); } } private static void Attach(FramebufferTarget target, Format format, int handle) { if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) { GL.FramebufferTexture(target, FramebufferAttachment.DepthStencilAttachment, handle, 0); } else if (IsDepthOnly(format)) { GL.FramebufferTexture(target, FramebufferAttachment.DepthAttachment, handle, 0); } else if (format == Format.S8Uint) { GL.FramebufferTexture(target, FramebufferAttachment.StencilAttachment, handle, 0); } else { GL.FramebufferTexture(target, FramebufferAttachment.ColorAttachment0, handle, 0); } } private static ClearBufferMask GetMask(Format format) { if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) { return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit; } else if (IsDepthOnly(format)) { return ClearBufferMask.DepthBufferBit; } else if (format == Format.S8Uint) { return ClearBufferMask.StencilBufferBit; } else { return ClearBufferMask.ColorBufferBit; } } private static bool IsDepthOnly(Format format) { return format == Format.D16Unorm || format == Format.D24X8Unorm || format == Format.D32Float; } public TextureView BgraSwap(TextureView from) { TextureView to = (TextureView)_renderer.CreateTexture(from.Info, 1f); EnsurePbo(from); GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle); from.WriteToPbo(0, forceBgra: true); GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPboHandle); to.ReadFromPbo(0, _copyPboSize); GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); return to; } private void EnsurePbo(TextureView view) { int requiredSize = 0; for (int level = 0; level < view.Info.Levels; level++) { requiredSize += view.Info.GetMipSize(level); } if (_copyPboSize < requiredSize && _copyPboHandle != 0) { GL.DeleteBuffer(_copyPboHandle); _copyPboHandle = 0; } if (_copyPboHandle == 0) { _copyPboHandle = GL.GenBuffer(); _copyPboSize = requiredSize; GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle); GL.BufferData(BufferTarget.PixelPackBuffer, requiredSize, IntPtr.Zero, BufferUsageHint.DynamicCopy); } } private int GetSrcFramebufferLazy() { if (_srcFramebuffer == 0) { _srcFramebuffer = GL.GenFramebuffer(); } return _srcFramebuffer; } private int GetDstFramebufferLazy() { if (_dstFramebuffer == 0) { _dstFramebuffer = GL.GenFramebuffer(); } return _dstFramebuffer; } public void Dispose() { if (_srcFramebuffer != 0) { GL.DeleteFramebuffer(_srcFramebuffer); _srcFramebuffer = 0; } if (_dstFramebuffer != 0) { GL.DeleteFramebuffer(_dstFramebuffer); _dstFramebuffer = 0; } if (_copyPboHandle != 0) { GL.DeleteBuffer(_copyPboHandle); _copyPboHandle = 0; } } } }